diff --git a/docs/api.md b/docs/api.md index 8586f78302..76037706b1 100644 --- a/docs/api.md +++ b/docs/api.md @@ -1170,7 +1170,7 @@ _Default:_ `true` if the [`shell`](#optionsshell) option is `true`, `false` othe If `false`, escapes the command arguments on Windows. -[More info.](windows.md#cmdexe-escaping) +[More info.](windows.md#escaping) ## Verbose function diff --git a/docs/windows.md b/docs/windows.md index cfb4db30b2..9990e490eb 100644 --- a/docs/windows.md +++ b/docs/windows.md @@ -34,22 +34,33 @@ The default value for the [`stdin`](api.md#optionsstdin), [`stdout`](api.md#opti Instead of `'pipe'`, `'overlapped'` can be used instead to use [asynchronous I/O](https://learn.microsoft.com/en-us/windows/win32/fileio/synchronous-and-asynchronous-i-o) under-the-hood on Windows, instead of the default behavior which is synchronous. On other platforms, asynchronous I/O is always used, so `'overlapped'` behaves the same way as `'pipe'`. -## Powershell +## Escaping -Windows typically requires files and arguments to be quoted when they contain spaces, tabs, backslashes or double quotes. Execa performs that quoting automatically. +Windows requires files and arguments to be quoted when they contain spaces, tabs, backslashes or double quotes. Unlike Unix, this is needed even when no [shell](shell.md) is used. -However, when using a [`cmd.exe`](https://en.wikipedia.org/wiki/Cmd.exe) shell (for example with the [`shell: true`](api.md#optionsshell) option), users must manually perform that quoting instead. It mostly involves double quoting arguments, and prepending double quotes with a backslash. The quoting rules are different from Unix shells. +When not using any shell, Execa performs that quoting automatically. This ensures files and arguments are split correctly. + +```js +await execa`npm run ${'task with space'}`; +``` + +When using a [shell](shell.md), the user must manually perform shell-specific quoting, on both Unix and Windows. When the [`shell`](api.md#optionsshell) option is `true`, [`cmd.exe`](https://en.wikipedia.org/wiki/Cmd.exe) is used on Windows and `sh` on Unix. Unfortunately both shells use different quoting rules. With `cmd.exe`, this mostly involves double quoting arguments and prepending double quotes with a backslash. ```js if (isWindows) { - await execa({shell: true})`npm run "task with space"`; + await execa({shell: true})`npm run ${'"task with space"'}`; +} else { + await execa({shell: true})`npm run ${'\'task with space\''}`; } ``` -When using other Windows shells (such as Powershell), that `cmd.exe`-specific automatic quoting must be manually disabled using -the [`windowsVerbatimArguments: true`](api.md#optionswindowsverbatimarguments) option. +When using other Windows shells (such as Powershell or WSL), Execa performs `cmd.exe`-specific automatic quoting by default. This is a problem since Powershell uses different quoting rules. This can be disabled using the [`windowsVerbatimArguments: true`](api.md#optionswindowsverbatimarguments) option. -Please note that the automatic quoting performed by `windowsVerbatimArguments: false` only helps with ensuring files and arguments are split correctly. It does not escape every possible `cmd.exe`-specific shell character. Therefore, to prevent against command injection, [`shell: false`](shell.md) should be used. +```js +if (isWindows) { + await execa({windowsVerbatimArguments: true})`wsl ...`; +} +``` ## Console window diff --git a/test/arguments/cwd.js b/test/arguments/cwd.js deleted file mode 100644 index 060731aeb0..0000000000 --- a/test/arguments/cwd.js +++ /dev/null @@ -1,104 +0,0 @@ -import {mkdir, rmdir} from 'node:fs/promises'; -import path from 'node:path'; -import process from 'node:process'; -import {pathToFileURL, fileURLToPath} from 'node:url'; -import tempfile from 'tempfile'; -import test from 'ava'; -import {execa, execaSync} from '../../index.js'; -import {FIXTURES_DIRECTORY, setFixtureDirectory} from '../helpers/fixtures-directory.js'; - -setFixtureDirectory(); - -const isWindows = process.platform === 'win32'; - -const testOptionCwdString = async (t, execaMethod) => { - const cwd = '/'; - const {stdout} = await execaMethod('node', ['-p', 'process.cwd()'], {cwd}); - t.is(path.toNamespacedPath(stdout), path.toNamespacedPath(cwd)); -}; - -test('The "cwd" option can be a string', testOptionCwdString, execa); -test('The "cwd" option can be a string - sync', testOptionCwdString, execaSync); - -const testOptionCwdUrl = async (t, execaMethod) => { - const cwd = '/'; - const cwdUrl = pathToFileURL(cwd); - const {stdout} = await execaMethod('node', ['-p', 'process.cwd()'], {cwd: cwdUrl}); - t.is(path.toNamespacedPath(stdout), path.toNamespacedPath(cwd)); -}; - -test('The "cwd" option can be a URL', testOptionCwdUrl, execa); -test('The "cwd" option can be a URL - sync', testOptionCwdUrl, execaSync); - -const testOptionCwdInvalid = (t, execaMethod) => { - t.throws(() => { - execaMethod('empty.js', {cwd: true}); - }, {message: /The "cwd" option must be a string or a file URL: true/}); -}; - -test('The "cwd" option cannot be an invalid type', testOptionCwdInvalid, execa); -test('The "cwd" option cannot be an invalid type - sync', testOptionCwdInvalid, execaSync); - -const testErrorCwdDefault = async (t, execaMethod) => { - const {cwd} = await execaMethod('empty.js'); - t.is(cwd, process.cwd()); -}; - -test('The "cwd" option defaults to process.cwd()', testErrorCwdDefault, execa); -test('The "cwd" option defaults to process.cwd() - sync', testErrorCwdDefault, execaSync); - -// Windows does not allow removing a directory used as `cwd` of a running subprocess -if (!isWindows) { - const testCwdPreSpawn = async (t, execaMethod) => { - const currentCwd = process.cwd(); - const filePath = tempfile(); - await mkdir(filePath); - process.chdir(filePath); - await rmdir(filePath); - - try { - t.throws(() => { - execaMethod('empty.js'); - }, {message: /The current directory does not exist/}); - } finally { - process.chdir(currentCwd); - } - }; - - test.serial('The "cwd" option default fails if current cwd is missing', testCwdPreSpawn, execa); - test.serial('The "cwd" option default fails if current cwd is missing - sync', testCwdPreSpawn, execaSync); -} - -const cwdNotExisting = {cwd: 'does_not_exist', expectedCode: 'ENOENT', expectedMessage: 'The "cwd" option is invalid'}; -const cwdTooLong = {cwd: '.'.repeat(1e5), expectedCode: 'ENAMETOOLONG', expectedMessage: 'The "cwd" option is invalid'}; -// @todo: use import.meta.dirname after dropping support for Node <20.11.0 -const cwdNotDirectory = {cwd: fileURLToPath(import.meta.url), expectedCode: isWindows ? 'ENOENT' : 'ENOTDIR', expectedMessage: 'The "cwd" option is not a directory'}; - -const testCwdPostSpawn = async (t, {cwd, expectedCode, expectedMessage}, execaMethod) => { - const {failed, code, message} = await execaMethod('empty.js', {cwd, reject: false}); - t.true(failed); - t.is(code, expectedCode); - t.true(message.includes(expectedMessage)); - t.true(message.includes(cwd)); -}; - -test('The "cwd" option must be an existing file', testCwdPostSpawn, cwdNotExisting, execa); -test('The "cwd" option must be an existing file - sync', testCwdPostSpawn, cwdNotExisting, execaSync); -test('The "cwd" option must not be too long', testCwdPostSpawn, cwdTooLong, execa); -test('The "cwd" option must not be too long - sync', testCwdPostSpawn, cwdTooLong, execaSync); -test('The "cwd" option must be a directory', testCwdPostSpawn, cwdNotDirectory, execa); -test('The "cwd" option must be a directory - sync', testCwdPostSpawn, cwdNotDirectory, execaSync); - -const successProperties = {fixtureName: 'empty.js', expectedFailed: false}; -const errorProperties = {fixtureName: 'fail.js', expectedFailed: true}; - -const testErrorCwd = async (t, execaMethod, {fixtureName, expectedFailed}) => { - const {failed, cwd} = await execaMethod(fixtureName, {cwd: path.relative('.', FIXTURES_DIRECTORY), reject: false}); - t.is(failed, expectedFailed); - t.is(cwd, FIXTURES_DIRECTORY); -}; - -test('result.cwd is defined', testErrorCwd, execa, successProperties); -test('result.cwd is defined - sync', testErrorCwd, execaSync, successProperties); -test('error.cwd is defined', testErrorCwd, execa, errorProperties); -test('error.cwd is defined - sync', testErrorCwd, execaSync, errorProperties); diff --git a/test/arguments/encoding-option.js b/test/arguments/encoding-option.js deleted file mode 100644 index e1f4073e49..0000000000 --- a/test/arguments/encoding-option.js +++ /dev/null @@ -1,39 +0,0 @@ -import test from 'ava'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; - -setFixtureDirectory(); - -const testInvalidEncoding = (t, encoding, message, execaMethod) => { - const error = t.throws(() => { - execaMethod('empty.js', {encoding}); - }); - t.true(error.message.includes(message)); -}; - -const UNKNOWN_ENCODING_MESSAGE = 'Please rename it to one of'; -const getCorrectEncodingMessage = correctEncoding => `Please rename it to "${correctEncoding}"`; - -test('cannot pass unknown encodings', testInvalidEncoding, 'unknown', UNKNOWN_ENCODING_MESSAGE, execa); -test('cannot pass unknown encodings, sync', testInvalidEncoding, 'unknown', UNKNOWN_ENCODING_MESSAGE, execaSync); -test('cannot pass empty encodings', testInvalidEncoding, '', UNKNOWN_ENCODING_MESSAGE, execa); -test('cannot pass encoding: false', testInvalidEncoding, false, UNKNOWN_ENCODING_MESSAGE, execa); -test('cannot pass encoding: Symbol', testInvalidEncoding, Symbol('test'), UNKNOWN_ENCODING_MESSAGE, execa); -test('cannot pass encoding: null', testInvalidEncoding, null, getCorrectEncodingMessage('buffer'), execa); -test('cannot pass encoding: null, sync', testInvalidEncoding, null, getCorrectEncodingMessage('buffer'), execaSync); -/* eslint-disable unicorn/text-encoding-identifier-case */ -test('cannot pass encoding: utf-8', testInvalidEncoding, 'utf-8', getCorrectEncodingMessage('utf8'), execa); -test('cannot pass encoding: utf-8, sync', testInvalidEncoding, 'utf-8', getCorrectEncodingMessage('utf8'), execaSync); -test('cannot pass encoding: UTF-8', testInvalidEncoding, 'UTF-8', getCorrectEncodingMessage('utf8'), execa); -test('cannot pass encoding: UTF-8, sync', testInvalidEncoding, 'UTF-8', getCorrectEncodingMessage('utf8'), execaSync); -test('cannot pass encoding: UTF8', testInvalidEncoding, 'UTF8', getCorrectEncodingMessage('utf8'), execa); -test('cannot pass encoding: UTF8, sync', testInvalidEncoding, 'UTF8', getCorrectEncodingMessage('utf8'), execaSync); -/* eslint-enable unicorn/text-encoding-identifier-case */ -test('cannot pass encoding: utf-16le', testInvalidEncoding, 'utf-16le', getCorrectEncodingMessage('utf16le'), execa); -test('cannot pass encoding: UTF-16LE', testInvalidEncoding, 'UTF-16LE', getCorrectEncodingMessage('utf16le'), execa); -test('cannot pass encoding: UTF16LE', testInvalidEncoding, 'UTF16LE', getCorrectEncodingMessage('utf16le'), execa); -test('cannot pass encoding: ucs2', testInvalidEncoding, 'ucs2', getCorrectEncodingMessage('utf16le'), execa); -test('cannot pass encoding: UCS2', testInvalidEncoding, 'UCS2', getCorrectEncodingMessage('utf16le'), execa); -test('cannot pass encoding: ucs-2', testInvalidEncoding, 'ucs-2', getCorrectEncodingMessage('utf16le'), execa); -test('cannot pass encoding: UCS-2', testInvalidEncoding, 'UCS-2', getCorrectEncodingMessage('utf16le'), execa); -test('cannot pass encoding: binary', testInvalidEncoding, 'binary', getCorrectEncodingMessage('latin1'), execa); diff --git a/test/arguments/env.js b/test/arguments/env.js deleted file mode 100644 index da9d25880a..0000000000 --- a/test/arguments/env.js +++ /dev/null @@ -1,32 +0,0 @@ -import process from 'node:process'; -import test from 'ava'; -import {execa} from '../../index.js'; -import {setFixtureDirectory, PATH_KEY} from '../helpers/fixtures-directory.js'; - -setFixtureDirectory(); -process.env.FOO = 'foo'; - -const isWindows = process.platform === 'win32'; - -test('use environment variables by default', async t => { - const {stdout} = await execa('environment.js'); - t.deepEqual(stdout.split('\n'), ['foo', 'undefined']); -}); - -test('extend environment variables by default', async t => { - const {stdout} = await execa('environment.js', [], {env: {BAR: 'bar', [PATH_KEY]: process.env[PATH_KEY]}}); - t.deepEqual(stdout.split('\n'), ['foo', 'bar']); -}); - -test('do not extend environment with `extendEnv: false`', async t => { - const {stdout} = await execa('environment.js', [], {env: {BAR: 'bar', [PATH_KEY]: process.env[PATH_KEY]}, extendEnv: false}); - t.deepEqual(stdout.split('\n'), ['undefined', 'bar']); -}); - -test('use extend environment with `extendEnv: true` and `shell: true`', async t => { - process.env.TEST = 'test'; - const command = isWindows ? 'echo %TEST%' : 'echo $TEST'; - const {stdout} = await execa(command, {shell: true, env: {}, extendEnv: true}); - t.is(stdout, 'test'); - delete process.env.TEST; -}); diff --git a/test/arguments/escape-no-icu.js b/test/arguments/escape-no-icu.js deleted file mode 100644 index 424abb5d38..0000000000 --- a/test/arguments/escape-no-icu.js +++ /dev/null @@ -1,16 +0,0 @@ -// Mimics Node.js when built without ICU support -// See https://github.com/sindresorhus/execa/issues/1143 -globalThis.RegExp = class extends RegExp { - constructor(regExpString, flags) { - if (flags?.includes('u') && regExpString.includes('\\p{')) { - throw new Error('Invalid property name'); - } - - super(regExpString, flags); - } - - static isMocked = true; -}; - -// Execa computes the RegExp when first loaded, so we must delay this import -await import('./escape.js'); diff --git a/test/arguments/escape.js b/test/arguments/escape.js deleted file mode 100644 index f2e2d7560a..0000000000 --- a/test/arguments/escape.js +++ /dev/null @@ -1,103 +0,0 @@ -import {platform} from 'node:process'; -import test from 'ava'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; - -setFixtureDirectory(); - -const isWindows = platform === 'win32'; - -const testResultCommand = async (t, expected, ...commandArguments) => { - const {command: failCommand} = await t.throwsAsync(execa('fail.js', commandArguments)); - t.is(failCommand, `fail.js${expected}`); - - const {command} = await execa('noop.js', commandArguments); - t.is(command, `noop.js${expected}`); -}; - -testResultCommand.title = (message, expected) => `result.command is: ${JSON.stringify(expected)}`; - -test(testResultCommand, ' foo bar', 'foo', 'bar'); -test(testResultCommand, ' baz quz', 'baz', 'quz'); -test(testResultCommand, ''); - -// eslint-disable-next-line max-params -const testEscapedCommand = async (t, commandArguments, expectedUnix, expectedWindows, expectedUnixNoIcu = expectedUnix, expectedWindowsNoIcu = expectedWindows) => { - const expected = RegExp.isMocked - ? (isWindows ? expectedWindowsNoIcu : expectedUnixNoIcu) - : (isWindows ? expectedWindows : expectedUnix); - - t.like( - await t.throwsAsync(execa('fail.js', commandArguments)), - {escapedCommand: `fail.js ${expected}`}, - ); - - t.like(t.throws(() => { - execaSync('fail.js', commandArguments); - }), {escapedCommand: `fail.js ${expected}`}); - - t.like( - await execa('noop.js', commandArguments), - {escapedCommand: `noop.js ${expected}`}, - ); - - t.like( - execaSync('noop.js', commandArguments), - {escapedCommand: `noop.js ${expected}`}, - ); -}; - -test('result.escapedCommand - foo bar', testEscapedCommand, ['foo', 'bar'], 'foo bar', 'foo bar'); -test('result.escapedCommand - foo\\ bar', testEscapedCommand, ['foo bar'], '\'foo bar\'', '"foo bar"'); -test('result.escapedCommand - "foo"', testEscapedCommand, ['"foo"'], '\'"foo"\'', '"""foo"""'); -test('result.escapedCommand - \'foo\'', testEscapedCommand, ['\'foo\''], '\'\'\\\'\'foo\'\\\'\'\'', '"\'foo\'"'); -test('result.escapedCommand - "0"', testEscapedCommand, ['0'], '0', '0'); -test('result.escapedCommand - 0', testEscapedCommand, [0], '0', '0'); -test('result.escapedCommand - *', testEscapedCommand, ['*'], '\'*\'', '"*"'); -test('result.escapedCommand - .', testEscapedCommand, ['.'], '.', '.'); -test('result.escapedCommand - -', testEscapedCommand, ['-'], '-', '-'); -test('result.escapedCommand - _', testEscapedCommand, ['_'], '_', '_'); -test('result.escapedCommand - /', testEscapedCommand, ['/'], '/', '/'); -test('result.escapedCommand - ,', testEscapedCommand, [','], '\',\'', '","'); -test('result.escapedCommand - :', testEscapedCommand, [':'], '\':\'', '":"'); -test('result.escapedCommand - ;', testEscapedCommand, [';'], '\';\'', '";"'); -test('result.escapedCommand - ~', testEscapedCommand, ['~'], '\'~\'', '"~"'); -test('result.escapedCommand - %', testEscapedCommand, ['%'], '\'%\'', '"%"'); -test('result.escapedCommand - $', testEscapedCommand, ['$'], '\'$\'', '"$"'); -test('result.escapedCommand - !', testEscapedCommand, ['!'], '\'!\'', '"!"'); -test('result.escapedCommand - ?', testEscapedCommand, ['?'], '\'?\'', '"?"'); -test('result.escapedCommand - #', testEscapedCommand, ['#'], '\'#\'', '"#"'); -test('result.escapedCommand - &', testEscapedCommand, ['&'], '\'&\'', '"&"'); -test('result.escapedCommand - =', testEscapedCommand, ['='], '\'=\'', '"="'); -test('result.escapedCommand - @', testEscapedCommand, ['@'], '\'@\'', '"@"'); -test('result.escapedCommand - ^', testEscapedCommand, ['^'], '\'^\'', '"^"'); -test('result.escapedCommand - `', testEscapedCommand, ['`'], '\'`\'', '"`"'); -test('result.escapedCommand - |', testEscapedCommand, ['|'], '\'|\'', '"|"'); -test('result.escapedCommand - +', testEscapedCommand, ['+'], '\'+\'', '"+"'); -test('result.escapedCommand - \\', testEscapedCommand, ['\\'], '\'\\\'', '"\\"'); -test('result.escapedCommand - ()', testEscapedCommand, ['()'], '\'()\'', '"()"'); -test('result.escapedCommand - {}', testEscapedCommand, ['{}'], '\'{}\'', '"{}"'); -test('result.escapedCommand - []', testEscapedCommand, ['[]'], '\'[]\'', '"[]"'); -test('result.escapedCommand - <>', testEscapedCommand, ['<>'], '\'<>\'', '"<>"'); -test('result.escapedCommand - ã', testEscapedCommand, ['ã'], '\'ã\'', '"ã"'); -test('result.escapedCommand - \\a', testEscapedCommand, ['\u0007'], '\'\\u0007\'', '"\\u0007"'); -test('result.escapedCommand - \\b', testEscapedCommand, ['\b'], '\'\\b\'', '"\\b"'); -test('result.escapedCommand - \\e', testEscapedCommand, ['\u001B'], '\'\\u001b\'', '"\\u001b"'); -test('result.escapedCommand - \\f', testEscapedCommand, ['\f'], '\'\\f\'', '"\\f"'); -test('result.escapedCommand - \\n', testEscapedCommand, ['\n'], '\'\\n\'', '"\\n"'); -test('result.escapedCommand - \\r\\n', testEscapedCommand, ['\r\n'], '\'\\r\\n\'', '"\\r\\n"'); -test('result.escapedCommand - \\t', testEscapedCommand, ['\t'], '\'\\t\'', '"\\t"'); -test('result.escapedCommand - \\v', testEscapedCommand, ['\v'], '\'\\u000b\'', '"\\u000b"'); -test('result.escapedCommand - \\x01', testEscapedCommand, ['\u0001'], '\'\\u0001\'', '"\\u0001"'); -test('result.escapedCommand - \\x7f', testEscapedCommand, ['\u007F'], '\'\\u007f\'', '"\\u007f"'); -test('result.escapedCommand - \\u0085', testEscapedCommand, ['\u0085'], '\'\\u0085\'', '"\\u0085"'); -test('result.escapedCommand - \\u2000', testEscapedCommand, ['\u2000'], '\'\\u2000\'', '"\\u2000"'); -test('result.escapedCommand - \\u200E', testEscapedCommand, ['\u200E'], '\'\\u200e\'', '"\\u200e"', '\'\u200E\'', '"\u200E"'); -test('result.escapedCommand - \\u2028', testEscapedCommand, ['\u2028'], '\'\\u2028\'', '"\\u2028"'); -test('result.escapedCommand - \\u2029', testEscapedCommand, ['\u2029'], '\'\\u2029\'', '"\\u2029"'); -test('result.escapedCommand - \\u5555', testEscapedCommand, ['\u5555'], '\'\u5555\'', '"\u5555"'); -test('result.escapedCommand - \\uD800', testEscapedCommand, ['\uD800'], '\'\\ud800\'', '"\\ud800"', '\'\uD800\'', '"\uD800"'); -test('result.escapedCommand - \\uE000', testEscapedCommand, ['\uE000'], '\'\\ue000\'', '"\\ue000"', '\'\uE000\'', '"\uE000"'); -test('result.escapedCommand - \\U1D172', testEscapedCommand, ['\u{1D172}'], '\'\u{1D172}\'', '"\u{1D172}"'); -test('result.escapedCommand - \\U1D173', testEscapedCommand, ['\u{1D173}'], '\'\\U1d173\'', '"\\U1d173"', '\'\u{1D173}\'', '"\u{1D173}"'); -test('result.escapedCommand - \\U10FFFD', testEscapedCommand, ['\u{10FFFD}'], '\'\\U10fffd\'', '"\\U10fffd"', '\'\u{10FFFD}\'', '"\u{10FFFD}"'); diff --git a/test/arguments/fd-options.js b/test/arguments/fd-options.js deleted file mode 100644 index b1653cf5f0..0000000000 --- a/test/arguments/fd-options.js +++ /dev/null @@ -1,739 +0,0 @@ -import {PassThrough} from 'node:stream'; -import {spawn} from 'node:child_process'; -import process from 'node:process'; -import test from 'ava'; -import {execa} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {fullStdio, getStdio} from '../helpers/stdio.js'; -import {getEarlyErrorSubprocess} from '../helpers/early-error.js'; -import {assertPipeError} from '../helpers/pipe.js'; - -setFixtureDirectory(); - -const getMessage = message => Array.isArray(message) - ? `"${message[0]}: ${message[1]}" option is incompatible` - : message; - -const testPipeError = async (t, { - message, - sourceOptions = {}, - destinationOptions = {}, - getSource = () => execa('empty.js', sourceOptions), - getDestination = () => execa('empty.js', destinationOptions), - isScript = false, - from, - to, -}) => { - const source = getSource(); - const pipePromise = isScript ? source.pipe({from, to})`empty.js` : source.pipe(getDestination(), {from, to}); - await assertPipeError(t, pipePromise, getMessage(message)); -}; - -const testNodeStream = async (t, { - message, - sourceOptions = {}, - getSource = () => execa('empty.js', sourceOptions), - from, - to, - writable = to !== undefined, -}) => { - assertNodeStream({ - t, - message, - getSource, - from, - to, - methodName: writable ? 'writable' : 'readable', - }); - assertNodeStream({ - t, - message, - getSource, - from, - to, - methodName: 'duplex', - }); -}; - -const assertNodeStream = ({t, message, getSource, from, to, methodName}) => { - const error = t.throws(() => { - getSource()[methodName]({from, to}); - }); - t.true(error.message.includes(getMessage(message))); -}; - -const testIterable = async (t, { - message, - sourceOptions = {}, - getSource = () => execa('empty.js', sourceOptions), - from, -}) => { - const error = t.throws(() => { - getSource().iterable({from}); - }); - t.true(error.message.includes(getMessage(message))); -}; - -test('Must set "all" option to "true" to use .pipe("all")', testPipeError, { - from: 'all', - message: '"all" option must be true', -}); -test('Must set "all" option to "true" to use .duplex("all")', testNodeStream, { - from: 'all', - message: '"all" option must be true', -}); -test('Must set "all" option to "true" to use .iterable("all")', testIterable, { - from: 'all', - message: '"all" option must be true', -}); -test('.pipe() cannot pipe to non-subprocesses', testPipeError, { - getDestination: () => new PassThrough(), - message: 'an Execa subprocess', -}); -test('.pipe() cannot pipe to non-Execa subprocesses', testPipeError, { - getDestination: () => spawn('node', ['--version']), - message: 'an Execa subprocess', -}); -test('.pipe() "from" option cannot be "stdin"', testPipeError, { - from: 'stdin', - message: '"from" must not be', -}); -test('.duplex() "from" option cannot be "stdin"', testNodeStream, { - from: 'stdin', - message: '"from" must not be', -}); -test('.iterable() "from" option cannot be "stdin"', testIterable, { - from: 'stdin', - message: '"from" must not be', -}); -test('$.pipe() "from" option cannot be "stdin"', testPipeError, { - from: 'stdin', - isScript: true, - message: '"from" must not be', -}); -test('.pipe() "to" option cannot be "stdout"', testPipeError, { - to: 'stdout', - message: '"to" must not be', -}); -test('.duplex() "to" option cannot be "stdout"', testNodeStream, { - to: 'stdout', - message: '"to" must not be', -}); -test('$.pipe() "to" option cannot be "stdout"', testPipeError, { - to: 'stdout', - isScript: true, - message: '"to" must not be', -}); -test('.pipe() "from" option cannot be any string', testPipeError, { - from: 'other', - message: 'must be "stdout", "stderr", "all"', -}); -test('.duplex() "from" option cannot be any string', testNodeStream, { - from: 'other', - message: 'must be "stdout", "stderr", "all"', -}); -test('.iterable() "from" option cannot be any string', testIterable, { - from: 'other', - message: 'must be "stdout", "stderr", "all"', -}); -test('.pipe() "to" option cannot be any string', testPipeError, { - to: 'other', - message: 'must be "stdin"', -}); -test('.duplex() "to" option cannot be any string', testNodeStream, { - to: 'other', - message: 'must be "stdin"', -}); -test('.pipe() "from" option cannot be a number without "fd"', testPipeError, { - from: '1', - message: 'must be "stdout", "stderr", "all"', -}); -test('.duplex() "from" option cannot be a number without "fd"', testNodeStream, { - from: '1', - message: 'must be "stdout", "stderr", "all"', -}); -test('.iterable() "from" option cannot be a number without "fd"', testIterable, { - from: '1', - message: 'must be "stdout", "stderr", "all"', -}); -test('.pipe() "to" option cannot be a number without "fd"', testPipeError, { - to: '0', - message: 'must be "stdin"', -}); -test('.duplex() "to" option cannot be a number without "fd"', testNodeStream, { - to: '0', - message: 'must be "stdin"', -}); -test('.pipe() "from" option cannot be just "fd"', testPipeError, { - from: 'fd', - message: 'must be "stdout", "stderr", "all"', -}); -test('.duplex() "from" option cannot be just "fd"', testNodeStream, { - from: 'fd', - message: 'must be "stdout", "stderr", "all"', -}); -test('.iterable() "from" option cannot be just "fd"', testIterable, { - from: 'fd', - message: 'must be "stdout", "stderr", "all"', -}); -test('.pipe() "to" option cannot be just "fd"', testPipeError, { - to: 'fd', - message: 'must be "stdin"', -}); -test('.duplex() "to" option cannot be just "fd"', testNodeStream, { - to: 'fd', - message: 'must be "stdin"', -}); -test('.pipe() "from" option cannot be a float', testPipeError, { - from: 'fd1.5', - message: 'must be "stdout", "stderr", "all"', -}); -test('.duplex() "from" option cannot be a float', testNodeStream, { - from: 'fd1.5', - message: 'must be "stdout", "stderr", "all"', -}); -test('.iterable() "from" option cannot be a float', testIterable, { - from: 'fd1.5', - message: 'must be "stdout", "stderr", "all"', -}); -test('.pipe() "to" option cannot be a float', testPipeError, { - to: 'fd1.5', - message: 'must be "stdin"', -}); -test('.duplex() "to" option cannot be a float', testNodeStream, { - to: 'fd1.5', - message: 'must be "stdin"', -}); -test('.pipe() "from" option cannot be a negative number', testPipeError, { - from: 'fd-1', - message: 'must be "stdout", "stderr", "all"', -}); -test('.duplex() "from" option cannot be a negative number', testNodeStream, { - from: 'fd-1', - message: 'must be "stdout", "stderr", "all"', -}); -test('.iterable() "from" option cannot be a negative number', testIterable, { - from: 'fd-1', - message: 'must be "stdout", "stderr", "all"', -}); -test('.pipe() "to" option cannot be a negative number', testPipeError, { - to: 'fd-1', - message: 'must be "stdin"', -}); -test('.duplex() "to" option cannot be a negative number', testNodeStream, { - to: 'fd-1', - message: 'must be "stdin"', -}); -test('.pipe() "from" option cannot be a non-existing file descriptor', testPipeError, { - from: 'fd3', - message: 'file descriptor does not exist', -}); -test('.duplex() "from" cannot be a non-existing file descriptor', testNodeStream, { - from: 'fd3', - message: 'file descriptor does not exist', -}); -test('.iterable() "from" cannot be a non-existing file descriptor', testIterable, { - from: 'fd3', - message: 'file descriptor does not exist', -}); -test('.pipe() "to" option cannot be a non-existing file descriptor', testPipeError, { - to: 'fd3', - message: 'file descriptor does not exist', -}); -test('.duplex() "to" cannot be a non-existing file descriptor', testNodeStream, { - to: 'fd3', - message: 'file descriptor does not exist', -}); -test('.pipe() "from" option cannot be an input file descriptor', testPipeError, { - sourceOptions: getStdio(3, new Uint8Array()), - from: 'fd3', - message: 'must be a readable stream', -}); -test('.duplex() "from" option cannot be an input file descriptor', testNodeStream, { - sourceOptions: getStdio(3, new Uint8Array()), - from: 'fd3', - message: 'must be a readable stream', -}); -test('.iterable() "from" option cannot be an input file descriptor', testIterable, { - sourceOptions: getStdio(3, new Uint8Array()), - from: 'fd3', - message: 'must be a readable stream', -}); -test('.pipe() "to" option cannot be an output file descriptor', testPipeError, { - destinationOptions: fullStdio, - to: 'fd3', - message: 'must be a writable stream', -}); -test('.duplex() "to" option cannot be an output file descriptor', testNodeStream, { - sourceOptions: fullStdio, - to: 'fd3', - message: 'must be a writable stream', -}); -test('.pipe() "to" option cannot be "all"', testPipeError, { - destinationOptions: fullStdio, - to: 'all', - message: 'must be a writable stream', -}); -test('.duplex() "to" option cannot be "all"', testNodeStream, { - sourceOptions: fullStdio, - to: 'all', - message: 'must be a writable stream', -}); -test('Cannot set "stdout" option to "ignore" to use .pipe()', testPipeError, { - sourceOptions: {stdout: 'ignore'}, - message: ['stdout', '\'ignore\''], -}); -test('Cannot set "stdout" option to "ignore" to use .duplex()', testNodeStream, { - sourceOptions: {stdout: 'ignore'}, - message: ['stdout', '\'ignore\''], -}); -test('Cannot set "stdout" option to "ignore" to use .iterable()', testIterable, { - sourceOptions: {stdout: 'ignore'}, - message: ['stdout', '\'ignore\''], -}); -test('Cannot set "stdin" option to "ignore" to use .pipe()', testPipeError, { - destinationOptions: {stdin: 'ignore'}, - message: ['stdin', '\'ignore\''], -}); -test('Cannot set "stdin" option to "ignore" to use .duplex()', testNodeStream, { - sourceOptions: {stdin: 'ignore'}, - message: ['stdin', '\'ignore\''], - writable: true, -}); -test('Cannot set "stdout" option to "ignore" to use .pipe(1)', testPipeError, { - sourceOptions: {stdout: 'ignore'}, - from: 'fd1', - message: ['stdout', '\'ignore\''], -}); -test('Cannot set "stdout" option to "ignore" to use .duplex(1)', testNodeStream, { - sourceOptions: {stdout: 'ignore'}, - from: 'fd1', - message: ['stdout', '\'ignore\''], -}); -test('Cannot set "stdout" option to "ignore" to use .iterable(1)', testIterable, { - sourceOptions: {stdout: 'ignore'}, - from: 'fd1', - message: ['stdout', '\'ignore\''], -}); -test('Cannot set "stdin" option to "ignore" to use .pipe(0)', testPipeError, { - destinationOptions: {stdin: 'ignore'}, - message: ['stdin', '\'ignore\''], - to: 'fd0', -}); -test('Cannot set "stdin" option to "ignore" to use .duplex(0)', testNodeStream, { - sourceOptions: {stdin: 'ignore'}, - message: ['stdin', '\'ignore\''], - to: 'fd0', -}); -test('Cannot set "stdout" option to "ignore" to use .pipe("stdout")', testPipeError, { - sourceOptions: {stdout: 'ignore'}, - from: 'stdout', - message: ['stdout', '\'ignore\''], -}); -test('Cannot set "stdout" option to "ignore" to use .duplex("stdout")', testNodeStream, { - sourceOptions: {stdout: 'ignore'}, - from: 'stdout', - message: ['stdout', '\'ignore\''], -}); -test('Cannot set "stdout" option to "ignore" to use .iterable("stdout")', testIterable, { - sourceOptions: {stdout: 'ignore'}, - from: 'stdout', - message: ['stdout', '\'ignore\''], -}); -test('Cannot set "stdin" option to "ignore" to use .pipe("stdin")', testPipeError, { - destinationOptions: {stdin: 'ignore'}, - message: ['stdin', '\'ignore\''], - to: 'stdin', -}); -test('Cannot set "stdin" option to "ignore" to use .duplex("stdin")', testNodeStream, { - sourceOptions: {stdin: 'ignore'}, - message: ['stdin', '\'ignore\''], - to: 'stdin', -}); -test('Cannot set "stdout" + "stderr" option to "ignore" to use .pipe()', testPipeError, { - sourceOptions: {stdout: 'ignore', stderr: 'ignore'}, - message: ['stdout', '\'ignore\''], -}); -test('Cannot set "stdout" + "stderr" option to "ignore" to use .duplex()', testNodeStream, { - sourceOptions: {stdout: 'ignore', stderr: 'ignore'}, - message: ['stdout', '\'ignore\''], -}); -test('Cannot set "stdout" + "stderr" option to "ignore" to use .iterable()', testIterable, { - sourceOptions: {stdout: 'ignore', stderr: 'ignore'}, - message: ['stdout', '\'ignore\''], -}); -test('Cannot set "stdout" + "stderr" option to "ignore" to use .pipe(1)', testPipeError, { - sourceOptions: {stdout: 'ignore', stderr: 'ignore'}, - from: 'fd1', - message: ['stdout', '\'ignore\''], -}); -test('Cannot set "stdout" + "stderr" option to "ignore" to use .duplex(1)', testNodeStream, { - sourceOptions: {stdout: 'ignore', stderr: 'ignore'}, - from: 'fd1', - message: ['stdout', '\'ignore\''], -}); -test('Cannot set "stdout" + "stderr" option to "ignore" to use .iterable(1)', testIterable, { - sourceOptions: {stdout: 'ignore', stderr: 'ignore'}, - from: 'fd1', - message: ['stdout', '\'ignore\''], -}); -test('Cannot set "stdout" + "stderr" option to "ignore" to use .pipe("stdout")', testPipeError, { - sourceOptions: {stdout: 'ignore', stderr: 'ignore'}, - from: 'stdout', - message: ['stdout', '\'ignore\''], -}); -test('Cannot set "stdout" + "stderr" option to "ignore" to use .duplex("stdout")', testNodeStream, { - sourceOptions: {stdout: 'ignore', stderr: 'ignore'}, - from: 'stdout', - message: ['stdout', '\'ignore\''], -}); -test('Cannot set "stdout" + "stderr" option to "ignore" to use .iterable("stdout")', testIterable, { - sourceOptions: {stdout: 'ignore', stderr: 'ignore'}, - from: 'stdout', - message: ['stdout', '\'ignore\''], -}); -test('Cannot set "stdio[1]" option to "ignore" to use .pipe()', testPipeError, { - sourceOptions: {stdio: ['pipe', 'ignore', 'pipe']}, - message: ['stdio[1]', '\'ignore\''], -}); -test('Cannot set "stdio[1]" option to "ignore" to use .duplex()', testNodeStream, { - sourceOptions: {stdio: ['pipe', 'ignore', 'pipe']}, - message: ['stdio[1]', '\'ignore\''], -}); -test('Cannot set "stdio[1]" option to "ignore" to use .iterable()', testIterable, { - sourceOptions: {stdio: ['pipe', 'ignore', 'pipe']}, - message: ['stdio[1]', '\'ignore\''], -}); -test('Cannot set "stdio[0]" option to "ignore" to use .pipe()', testPipeError, { - destinationOptions: {stdio: ['ignore', 'pipe', 'pipe']}, - message: ['stdio[0]', '\'ignore\''], -}); -test('Cannot set "stdio[0]" option to "ignore" to use .duplex()', testNodeStream, { - sourceOptions: {stdio: ['ignore', 'pipe', 'pipe']}, - message: ['stdio[0]', '\'ignore\''], - writable: true, -}); -test('Cannot set "stdio[1]" option to "ignore" to use .pipe(1)', testPipeError, { - sourceOptions: {stdio: ['pipe', 'ignore', 'pipe']}, - from: 'fd1', - message: ['stdio[1]', '\'ignore\''], -}); -test('Cannot set "stdio[1]" option to "ignore" to use .duplex(1)', testNodeStream, { - sourceOptions: {stdio: ['pipe', 'ignore', 'pipe']}, - from: 'fd1', - message: ['stdio[1]', '\'ignore\''], -}); -test('Cannot set "stdio[1]" option to "ignore" to use .iterable(1)', testIterable, { - sourceOptions: {stdio: ['pipe', 'ignore', 'pipe']}, - from: 'fd1', - message: ['stdio[1]', '\'ignore\''], -}); -test('Cannot set "stdio[0]" option to "ignore" to use .pipe(0)', testPipeError, { - destinationOptions: {stdio: ['ignore', 'pipe', 'pipe']}, - message: ['stdio[0]', '\'ignore\''], - to: 'fd0', -}); -test('Cannot set "stdio[0]" option to "ignore" to use .duplex(0)', testNodeStream, { - sourceOptions: {stdio: ['ignore', 'pipe', 'pipe']}, - message: ['stdio[0]', '\'ignore\''], - to: 'fd0', -}); -test('Cannot set "stdio[1]" option to "ignore" to use .pipe("stdout")', testPipeError, { - sourceOptions: {stdio: ['pipe', 'ignore', 'pipe']}, - from: 'stdout', - message: ['stdio[1]', '\'ignore\''], -}); -test('Cannot set "stdio[1]" option to "ignore" to use .duplex("stdout")', testNodeStream, { - sourceOptions: {stdio: ['pipe', 'ignore', 'pipe']}, - from: 'stdout', - message: ['stdio[1]', '\'ignore\''], -}); -test('Cannot set "stdio[1]" option to "ignore" to use .iterable("stdout")', testIterable, { - sourceOptions: {stdio: ['pipe', 'ignore', 'pipe']}, - from: 'stdout', - message: ['stdio[1]', '\'ignore\''], -}); -test('Cannot set "stdio[0]" option to "ignore" to use .pipe("stdin")', testPipeError, { - destinationOptions: {stdio: ['ignore', 'pipe', 'pipe']}, - message: ['stdio[0]', '\'ignore\''], - to: 'stdin', -}); -test('Cannot set "stdio[0]" option to "ignore" to use .duplex("stdin")', testNodeStream, { - sourceOptions: {stdio: ['ignore', 'pipe', 'pipe']}, - message: ['stdio[0]', '\'ignore\''], - to: 'stdin', -}); -test('Cannot set "stderr" option to "ignore" to use .pipe(2)', testPipeError, { - sourceOptions: {stderr: 'ignore'}, - from: 'fd2', - message: ['stderr', '\'ignore\''], -}); -test('Cannot set "stderr" option to "ignore" to use .duplex(2)', testNodeStream, { - sourceOptions: {stderr: 'ignore'}, - from: 'fd2', - message: ['stderr', '\'ignore\''], -}); -test('Cannot set "stderr" option to "ignore" to use .iterable(2)', testIterable, { - sourceOptions: {stderr: 'ignore'}, - from: 'fd2', - message: ['stderr', '\'ignore\''], -}); -test('Cannot set "stderr" option to "ignore" to use .pipe("stderr")', testPipeError, { - sourceOptions: {stderr: 'ignore'}, - from: 'stderr', - message: ['stderr', '\'ignore\''], -}); -test('Cannot set "stderr" option to "ignore" to use .duplex("stderr")', testNodeStream, { - sourceOptions: {stderr: 'ignore'}, - from: 'stderr', - message: ['stderr', '\'ignore\''], -}); -test('Cannot set "stderr" option to "ignore" to use .iterable("stderr")', testIterable, { - sourceOptions: {stderr: 'ignore'}, - from: 'stderr', - message: ['stderr', '\'ignore\''], -}); -test('Cannot set "stdout" + "stderr" option to "ignore" to use .pipe(2)', testPipeError, { - sourceOptions: {stdout: 'ignore', stderr: 'ignore'}, - from: 'fd2', - message: ['stderr', '\'ignore\''], -}); -test('Cannot set "stdout" + "stderr" option to "ignore" to use .duplex(2)', testNodeStream, { - sourceOptions: {stdout: 'ignore', stderr: 'ignore'}, - from: 'fd2', - message: ['stderr', '\'ignore\''], -}); -test('Cannot set "stdout" + "stderr" option to "ignore" to use .iterable(2)', testIterable, { - sourceOptions: {stdout: 'ignore', stderr: 'ignore'}, - from: 'fd2', - message: ['stderr', '\'ignore\''], -}); -test('Cannot set "stdout" + "stderr" option to "ignore" to use .pipe("stderr")', testPipeError, { - sourceOptions: {stdout: 'ignore', stderr: 'ignore'}, - from: 'stderr', - message: ['stderr', '\'ignore\''], -}); -test('Cannot set "stdout" + "stderr" option to "ignore" to use .duplex("stderr")', testNodeStream, { - sourceOptions: {stdout: 'ignore', stderr: 'ignore'}, - from: 'stderr', - message: ['stderr', '\'ignore\''], -}); -test('Cannot set "stdout" + "stderr" option to "ignore" to use .iterable("stderr")', testIterable, { - sourceOptions: {stdout: 'ignore', stderr: 'ignore'}, - from: 'stderr', - message: ['stderr', '\'ignore\''], -}); -test('Cannot set "stdio[2]" option to "ignore" to use .pipe(2)', testPipeError, { - sourceOptions: {stdio: ['pipe', 'pipe', 'ignore']}, - from: 'fd2', - message: ['stdio[2]', '\'ignore\''], -}); -test('Cannot set "stdio[2]" option to "ignore" to use .duplex(2)', testNodeStream, { - sourceOptions: {stdio: ['pipe', 'pipe', 'ignore']}, - from: 'fd2', - message: ['stdio[2]', '\'ignore\''], -}); -test('Cannot set "stdio[2]" option to "ignore" to use .iterable(2)', testIterable, { - sourceOptions: {stdio: ['pipe', 'pipe', 'ignore']}, - from: 'fd2', - message: ['stdio[2]', '\'ignore\''], -}); -test('Cannot set "stdio[2]" option to "ignore" to use .pipe("stderr")', testPipeError, { - sourceOptions: {stdio: ['pipe', 'pipe', 'ignore']}, - from: 'stderr', - message: ['stdio[2]', '\'ignore\''], -}); -test('Cannot set "stdio[2]" option to "ignore" to use .duplex("stderr")', testNodeStream, { - sourceOptions: {stdio: ['pipe', 'pipe', 'ignore']}, - from: 'stderr', - message: ['stdio[2]', '\'ignore\''], -}); -test('Cannot set "stdio[2]" option to "ignore" to use .iterable("stderr")', testIterable, { - sourceOptions: {stdio: ['pipe', 'pipe', 'ignore']}, - from: 'stderr', - message: ['stdio[2]', '\'ignore\''], -}); -test('Cannot set "stdio[3]" option to "ignore" to use .pipe(3)', testPipeError, { - sourceOptions: getStdio(3, 'ignore'), - from: 'fd3', - message: ['stdio[3]', '\'ignore\''], -}); -test('Cannot set "stdio[3]" option to "ignore" to use .duplex(3)', testNodeStream, { - sourceOptions: getStdio(3, 'ignore'), - from: 'fd3', - message: ['stdio[3]', '\'ignore\''], -}); -test('Cannot set "stdio[3]" option to "ignore" to use .iterable(3)', testIterable, { - sourceOptions: getStdio(3, 'ignore'), - from: 'fd3', - message: ['stdio[3]', '\'ignore\''], -}); -test('Cannot set "stdout" + "stderr" option to "ignore" to use .pipe("all")', testPipeError, { - sourceOptions: {stdout: 'ignore', stderr: 'ignore', all: true}, - from: 'all', - message: ['stdout', '\'ignore\''], -}); -test('Cannot set "stdout" + "stderr" option to "ignore" to use .duplex("all")', testNodeStream, { - sourceOptions: {stdout: 'ignore', stderr: 'ignore', all: true}, - from: 'all', - message: ['stdout', '\'ignore\''], -}); -test('Cannot set "stdout" + "stderr" option to "ignore" to use .iterable("all")', testIterable, { - sourceOptions: {stdout: 'ignore', stderr: 'ignore', all: true}, - from: 'all', - message: ['stdout', '\'ignore\''], -}); -test('Cannot set "stdio[1]" + "stdio[2]" option to "ignore" to use .pipe("all")', testPipeError, { - sourceOptions: {stdio: ['pipe', 'ignore', 'ignore'], all: true}, - from: 'all', - message: ['stdio[1]', '\'ignore\''], -}); -test('Cannot set "stdio[1]" + "stdio[2]" option to "ignore" to use .duplex("all")', testNodeStream, { - sourceOptions: {stdio: ['pipe', 'ignore', 'ignore'], all: true}, - from: 'all', - message: ['stdio[1]', '\'ignore\''], -}); -test('Cannot set "stdio[1]" + "stdio[2]" option to "ignore" to use .iterable("all")', testIterable, { - sourceOptions: {stdio: ['pipe', 'ignore', 'ignore'], all: true}, - from: 'all', - message: ['stdio[1]', '\'ignore\''], -}); -test('Cannot set "stdout" option to "inherit" to use .pipe()', testPipeError, { - sourceOptions: {stdout: 'inherit'}, - message: ['stdout', '\'inherit\''], -}); -test('Cannot set "stdout" option to "inherit" to use .duplex()', testNodeStream, { - sourceOptions: {stdout: 'inherit'}, - message: ['stdout', '\'inherit\''], -}); -test('Cannot set "stdout" option to "inherit" to use .iterable()', testIterable, { - sourceOptions: {stdout: 'inherit'}, - message: ['stdout', '\'inherit\''], -}); -test('Cannot set "stdin" option to "inherit" to use .pipe()', testPipeError, { - destinationOptions: {stdin: 'inherit'}, - message: ['stdin', '\'inherit\''], -}); -test('Cannot set "stdin" option to "inherit" to use .duplex()', testNodeStream, { - sourceOptions: {stdin: 'inherit'}, - message: ['stdin', '\'inherit\''], - writable: true, -}); -test('Cannot set "stdout" option to "ipc" to use .pipe()', testPipeError, { - sourceOptions: {stdout: 'ipc'}, - message: ['stdout', '\'ipc\''], -}); -test('Cannot set "stdout" option to "ipc" to use .duplex()', testNodeStream, { - sourceOptions: {stdout: 'ipc'}, - message: ['stdout', '\'ipc\''], -}); -test('Cannot set "stdout" option to "ipc" to use .iterable()', testIterable, { - sourceOptions: {stdout: 'ipc'}, - message: ['stdout', '\'ipc\''], -}); -test('Cannot set "stdin" option to "ipc" to use .pipe()', testPipeError, { - destinationOptions: {stdin: 'ipc'}, - message: ['stdin', '\'ipc\''], -}); -test('Cannot set "stdin" option to "ipc" to use .duplex()', testNodeStream, { - sourceOptions: {stdin: 'ipc'}, - message: ['stdin', '\'ipc\''], - writable: true, -}); -test('Cannot set "stdout" option to file descriptors to use .pipe()', testPipeError, { - sourceOptions: {stdout: 1}, - message: ['stdout', '1'], -}); -test('Cannot set "stdout" option to file descriptors to use .duplex()', testNodeStream, { - sourceOptions: {stdout: 1}, - message: ['stdout', '1'], -}); -test('Cannot set "stdout" option to file descriptors to use .iterable()', testIterable, { - sourceOptions: {stdout: 1}, - message: ['stdout', '1'], -}); -test('Cannot set "stdin" option to file descriptors to use .pipe()', testPipeError, { - destinationOptions: {stdin: 0}, - message: ['stdin', '0'], -}); -test('Cannot set "stdin" option to file descriptors to use .duplex()', testNodeStream, { - sourceOptions: {stdin: 0}, - message: ['stdin', '0'], - writable: true, -}); -test('Cannot set "stdout" option to Node.js streams to use .pipe()', testPipeError, { - sourceOptions: {stdout: process.stdout}, - message: ['stdout', 'Stream'], -}); -test('Cannot set "stdout" option to Node.js streams to use .duplex()', testNodeStream, { - sourceOptions: {stdout: process.stdout}, - message: ['stdout', 'Stream'], -}); -test('Cannot set "stdout" option to Node.js streams to use .iterable()', testIterable, { - sourceOptions: {stdout: process.stdout}, - message: ['stdout', 'Stream'], -}); -test('Cannot set "stdin" option to Node.js streams to use .pipe()', testPipeError, { - destinationOptions: {stdin: process.stdin}, - message: ['stdin', 'Stream'], -}); -test('Cannot set "stdin" option to Node.js streams to use .duplex()', testNodeStream, { - sourceOptions: {stdin: process.stdin}, - message: ['stdin', 'Stream'], - writable: true, -}); -test('Cannot set "stdio[3]" option to Node.js Writable streams to use .pipe()', testPipeError, { - sourceOptions: getStdio(3, process.stdout), - message: ['stdio[3]', 'Stream'], - from: 'fd3', -}); -test('Cannot set "stdio[3]" option to Node.js Writable streams to use .duplex()', testNodeStream, { - sourceOptions: getStdio(3, process.stdout), - message: ['stdio[3]', 'Stream'], - from: 'fd3', -}); -test('Cannot set "stdio[3]" option to Node.js Writable streams to use .iterable()', testIterable, { - sourceOptions: getStdio(3, process.stdout), - message: ['stdio[3]', 'Stream'], - from: 'fd3', -}); -test('Cannot set "stdio[3]" option to Node.js Readable streams to use .pipe()', testPipeError, { - destinationOptions: getStdio(3, process.stdin), - message: ['stdio[3]', 'Stream'], - to: 'fd3', -}); -test('Cannot set "stdio[3]" option to Node.js Readable streams to use .duplex()', testNodeStream, { - sourceOptions: getStdio(3, process.stdin), - message: ['stdio[3]', 'Stream'], - to: 'fd3', -}); - -test('Sets the right error message when the "all" option is incompatible - execa.$', async t => { - await assertPipeError( - t, - execa('empty.js') - .pipe({all: false})`stdin.js` - .pipe(execa('empty.js'), {from: 'all'}), - '"all" option must be true', - ); -}); - -test('Sets the right error message when the "all" option is incompatible - execa.execa', async t => { - await assertPipeError( - t, - execa('empty.js') - .pipe(execa('stdin.js', {all: false})) - .pipe(execa('empty.js'), {from: 'all'}), - '"all" option must be true', - ); -}); - -test('Sets the right error message when the "all" option is incompatible - early error', async t => { - await assertPipeError( - t, - getEarlyErrorSubprocess() - .pipe(execa('stdin.js', {all: false})) - .pipe(execa('empty.js'), {from: 'all'}), - '"all" option must be true', - ); -}); diff --git a/test/arguments/local.js b/test/arguments/local.js deleted file mode 100644 index 47136f33af..0000000000 --- a/test/arguments/local.js +++ /dev/null @@ -1,73 +0,0 @@ -import path from 'node:path'; -import process from 'node:process'; -import {pathToFileURL} from 'node:url'; -import test from 'ava'; -import {execa, $} from '../../index.js'; -import {setFixtureDirectory, PATH_KEY} from '../helpers/fixtures-directory.js'; - -setFixtureDirectory(); -process.env.FOO = 'foo'; - -const isWindows = process.platform === 'win32'; -const ENOENT_REGEXP = isWindows ? /failed with exit code 1/ : /spawn.* ENOENT/; - -const getPathWithoutLocalDirectory = () => { - const newPath = process.env[PATH_KEY] - .split(path.delimiter) - .filter(pathDirectory => !BIN_DIR_REGEXP.test(pathDirectory)).join(path.delimiter); - return {[PATH_KEY]: newPath}; -}; - -const BIN_DIR_REGEXP = /node_modules[\\/]\.bin/; - -const pathWitoutLocalDirectory = getPathWithoutLocalDirectory(); - -test('preferLocal: true', async t => { - await t.notThrowsAsync(execa('ava', ['--version'], {preferLocal: true, env: pathWitoutLocalDirectory})); -}); - -test('preferLocal: false', async t => { - await t.throwsAsync(execa('ava', ['--version'], {preferLocal: false, env: pathWitoutLocalDirectory}), {message: ENOENT_REGEXP}); -}); - -test('preferLocal: undefined', async t => { - await t.throwsAsync(execa('ava', ['--version'], {env: pathWitoutLocalDirectory}), {message: ENOENT_REGEXP}); -}); - -test('preferLocal: undefined with $', async t => { - await t.notThrowsAsync($('ava', ['--version'], {env: pathWitoutLocalDirectory})); -}); - -test('preferLocal: undefined with $.sync', t => { - t.notThrows(() => $.sync('ava', ['--version'], {env: pathWitoutLocalDirectory})); -}); - -test('preferLocal: undefined with execa.pipe`...`', async t => { - await t.throwsAsync(() => execa('node', ['--version']).pipe({env: pathWitoutLocalDirectory})`ava --version`); -}); - -test('preferLocal: undefined with $.pipe`...`', async t => { - await t.notThrows(() => $('node', ['--version']).pipe({env: pathWitoutLocalDirectory})`ava --version`); -}); - -test('preferLocal: undefined with execa.pipe()', async t => { - await t.throwsAsync(() => execa('node', ['--version']).pipe('ava', ['--version'], {env: pathWitoutLocalDirectory})); -}); - -test('preferLocal: undefined with $.pipe()', async t => { - await t.notThrows(() => $('node', ['--version']).pipe('ava', ['--version'], {env: pathWitoutLocalDirectory})); -}); - -test('localDir option', async t => { - const command = isWindows ? 'echo %PATH%' : 'echo $PATH'; - const {stdout} = await execa(command, {shell: true, preferLocal: true, localDir: '/test'}); - const envPaths = stdout.split(path.delimiter); - t.true(envPaths.some(envPath => envPath.endsWith('.bin'))); -}); - -test('localDir option can be a URL', async t => { - const command = isWindows ? 'echo %PATH%' : 'echo $PATH'; - const {stdout} = await execa(command, {shell: true, preferLocal: true, localDir: pathToFileURL('/test')}); - const envPaths = stdout.split(path.delimiter); - t.true(envPaths.some(envPath => envPath.endsWith('.bin'))); -}); diff --git a/test/arguments/shell.js b/test/arguments/shell.js deleted file mode 100644 index 7d80b4dfc1..0000000000 --- a/test/arguments/shell.js +++ /dev/null @@ -1,27 +0,0 @@ -import process from 'node:process'; -import {pathToFileURL} from 'node:url'; -import test from 'ava'; -import which from 'which'; -import {execa} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {identity} from '../helpers/stdio.js'; - -setFixtureDirectory(); -process.env.FOO = 'foo'; - -const isWindows = process.platform === 'win32'; - -test('can use `options.shell: true`', async t => { - const {stdout} = await execa('node test/fixtures/noop.js foo', {shell: true}); - t.is(stdout, 'foo'); -}); - -const testShellPath = async (t, mapPath) => { - const shellPath = isWindows ? 'cmd.exe' : 'bash'; - const shell = mapPath(await which(shellPath)); - const {stdout} = await execa('node test/fixtures/noop.js foo', {shell}); - t.is(stdout, 'foo'); -}; - -test('can use `options.shell: string`', testShellPath, identity); -test('can use `options.shell: file URL`', testShellPath, pathToFileURL); diff --git a/test/arguments/specific.js b/test/arguments/specific.js deleted file mode 100644 index f86d58673c..0000000000 --- a/test/arguments/specific.js +++ /dev/null @@ -1,38 +0,0 @@ -import test from 'ava'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString} from '../helpers/input.js'; - -setFixtureDirectory(); - -// eslint-disable-next-line max-params -const testPriorityOrder = async (t, buffer, bufferStdout, bufferStderr, execaMethod) => { - const {stdout, stderr} = await execaMethod('noop-both.js', {buffer}); - t.is(stdout, bufferStdout ? foobarString : undefined); - t.is(stderr, bufferStderr ? foobarString : undefined); -}; - -test('buffer: {stdout, fd1}', testPriorityOrder, {stdout: true, fd1: false}, true, true, execa); -test('buffer: {stdout, all}', testPriorityOrder, {stdout: true, all: false}, true, false, execa); -test('buffer: {fd1, all}', testPriorityOrder, {fd1: true, all: false}, true, false, execa); -test('buffer: {stderr, fd2}', testPriorityOrder, {stderr: true, fd2: false}, true, true, execa); -test('buffer: {stderr, all}', testPriorityOrder, {stderr: true, all: false}, false, true, execa); -test('buffer: {fd2, all}', testPriorityOrder, {fd2: true, all: false}, false, true, execa); -test('buffer: {fd1, stdout}', testPriorityOrder, {fd1: false, stdout: true}, true, true, execa); -test('buffer: {all, stdout}', testPriorityOrder, {all: false, stdout: true}, true, false, execa); -test('buffer: {all, fd1}', testPriorityOrder, {all: false, fd1: true}, true, false, execa); -test('buffer: {fd2, stderr}', testPriorityOrder, {fd2: false, stderr: true}, true, true, execa); -test('buffer: {all, stderr}', testPriorityOrder, {all: false, stderr: true}, false, true, execa); -test('buffer: {all, fd2}', testPriorityOrder, {all: false, fd2: true}, false, true, execa); -test('buffer: {stdout, fd1}, sync', testPriorityOrder, {stdout: true, fd1: false}, true, true, execaSync); -test('buffer: {stdout, all}, sync', testPriorityOrder, {stdout: true, all: false}, true, false, execaSync); -test('buffer: {fd1, all}, sync', testPriorityOrder, {fd1: true, all: false}, true, false, execaSync); -test('buffer: {stderr, fd2}, sync', testPriorityOrder, {stderr: true, fd2: false}, true, true, execaSync); -test('buffer: {stderr, all}, sync', testPriorityOrder, {stderr: true, all: false}, false, true, execaSync); -test('buffer: {fd2, all}, sync', testPriorityOrder, {fd2: true, all: false}, false, true, execaSync); -test('buffer: {fd1, stdout}, sync', testPriorityOrder, {fd1: false, stdout: true}, true, true, execaSync); -test('buffer: {all, stdout}, sync', testPriorityOrder, {all: false, stdout: true}, true, false, execaSync); -test('buffer: {all, fd1}, sync', testPriorityOrder, {all: false, fd1: true}, true, false, execaSync); -test('buffer: {fd2, stderr}, sync', testPriorityOrder, {fd2: false, stderr: true}, true, true, execaSync); -test('buffer: {all, stderr}, sync', testPriorityOrder, {all: false, stderr: true}, false, true, execaSync); -test('buffer: {all, fd2}, sync', testPriorityOrder, {all: false, fd2: true}, false, true, execaSync); diff --git a/test/convert/concurrent.js b/test/convert/concurrent.js deleted file mode 100644 index 3767f2d693..0000000000 --- a/test/convert/concurrent.js +++ /dev/null @@ -1,372 +0,0 @@ -import {setTimeout} from 'node:timers/promises'; -import test from 'ava'; -import {execa} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString} from '../helpers/input.js'; -import {fullReadableStdio} from '../helpers/stdio.js'; -import { - finishedStream, - assertStreamOutput, - assertStreamError, - assertStreamReadError, - assertSubprocessOutput, - assertSubprocessError, - getReadWriteSubprocess, -} from '../helpers/convert.js'; - -setFixtureDirectory(); - -const endStream = async stream => { - stream.end(foobarString); - await setTimeout(0); -}; - -// eslint-disable-next-line max-params -const endSameWritable = async (t, stream, secondStream, subprocess, fdNumber) => { - await endStream(stream); - t.true(subprocess.stdio[fdNumber].writable); - - await endStream(secondStream); - t.false(subprocess.stdio[fdNumber].writable); -}; - -// eslint-disable-next-line max-params -const endDifferentWritable = async (t, stream, secondStream, subprocess, fdNumber = 0, secondFdNumber = 3) => { - await endStream(stream); - t.false(subprocess.stdio[fdNumber].writable); - t.true(subprocess.stdio[secondFdNumber].writable); - - await endStream(secondStream); - t.false(subprocess.stdio[secondFdNumber].writable); -}; - -const testReadableTwice = async (t, fdNumber, from) => { - const subprocess = execa('noop-fd.js', [`${fdNumber}`, foobarString]); - const stream = subprocess.readable({from}); - const secondStream = subprocess.readable({from}); - - await Promise.all([ - assertStreamOutput(t, stream), - assertStreamOutput(t, secondStream), - ]); - await assertSubprocessOutput(t, subprocess, foobarString, fdNumber); -}; - -test('Can call .readable() twice on same file descriptor', testReadableTwice, 1); -test('Can call .readable({from: "stderr"}) twice on same file descriptor', testReadableTwice, 2, 'stderr'); - -const testWritableTwice = async (t, fdNumber, to, options) => { - const subprocess = execa('stdin-fd.js', [`${fdNumber}`], options); - const stream = subprocess.writable({to}); - const secondStream = subprocess.writable({to}); - - await Promise.all([ - finishedStream(stream), - finishedStream(secondStream), - endSameWritable(t, stream, secondStream, subprocess, fdNumber), - ]); - await assertSubprocessOutput(t, subprocess, `${foobarString}${foobarString}`); -}; - -test('Can call .writable() twice on same file descriptor', testWritableTwice, 0, undefined, {}); -test('Can call .writable({to: "fd3"}) twice on same file descriptor', testWritableTwice, 3, 'fd3', fullReadableStdio()); - -const testDuplexTwice = async (t, fdNumber, to, options) => { - const subprocess = execa('stdin-fd.js', [`${fdNumber}`], options); - const stream = subprocess.duplex({to}); - const secondStream = subprocess.duplex({to}); - - const expectedOutput = `${foobarString}${foobarString}`; - await Promise.all([ - assertStreamOutput(t, stream, expectedOutput), - assertStreamOutput(t, secondStream, expectedOutput), - endSameWritable(t, stream, secondStream, subprocess, fdNumber), - ]); - await assertSubprocessOutput(t, subprocess, expectedOutput); -}; - -test('Can call .duplex() twice on same file descriptor', testDuplexTwice, 0, undefined, {}); -test('Can call .duplex({to: "fd3"}) twice on same file descriptor', testDuplexTwice, 3, 'fd3', fullReadableStdio()); - -test('Can call .duplex() twice on same readable file descriptor but different writable one', async t => { - const subprocess = execa('stdin-fd-both.js', ['3'], fullReadableStdio()); - const stream = subprocess.duplex(); - const secondStream = subprocess.duplex({to: 'fd3'}); - - const expectedOutput = `${foobarString}${foobarString}`; - await Promise.all([ - assertStreamOutput(t, stream, expectedOutput), - assertStreamOutput(t, secondStream, expectedOutput), - endDifferentWritable(t, stream, secondStream, subprocess), - ]); - await assertSubprocessOutput(t, subprocess, expectedOutput); -}); - -test('Can call .readable() twice on different file descriptors', async t => { - const subprocess = execa('noop-both.js', [foobarString]); - const stream = subprocess.readable(); - const secondStream = subprocess.readable({from: 'stderr'}); - - const expectedOutput = `${foobarString}\n`; - await Promise.all([ - assertStreamOutput(t, stream, expectedOutput), - assertStreamOutput(t, secondStream, expectedOutput), - ]); - await assertSubprocessOutput(t, subprocess); - await assertSubprocessOutput(t, subprocess, foobarString, 2); -}); - -test('Can call .writable() twice on different file descriptors', async t => { - const subprocess = execa('stdin-fd-both.js', ['3'], fullReadableStdio()); - const stream = subprocess.writable(); - const secondStream = subprocess.writable({to: 'fd3'}); - - await Promise.all([ - finishedStream(stream), - finishedStream(secondStream), - endDifferentWritable(t, stream, secondStream, subprocess), - ]); - await assertSubprocessOutput(t, subprocess, `${foobarString}${foobarString}`); -}); - -test('Can call .duplex() twice on different file descriptors', async t => { - const subprocess = execa('stdin-twice-both.js', ['3'], fullReadableStdio()); - const stream = subprocess.duplex(); - const secondStream = subprocess.duplex({from: 'stderr', to: 'fd3'}); - - await Promise.all([ - assertStreamOutput(t, stream), - assertStreamOutput(t, secondStream), - endDifferentWritable(t, stream, secondStream, subprocess), - ]); - await assertSubprocessOutput(t, subprocess); - await assertSubprocessOutput(t, subprocess, foobarString, 2); -}); - -test('Can call .readable() and .writable()', async t => { - const subprocess = getReadWriteSubprocess(); - const stream = subprocess.writable(); - const secondStream = subprocess.readable(); - stream.end(foobarString); - - await Promise.all([ - finishedStream(stream), - assertStreamOutput(t, secondStream), - ]); - await assertSubprocessOutput(t, subprocess); -}); - -test('Can call .writable() and .duplex()', async t => { - const subprocess = execa('stdin-fd-both.js', ['3'], fullReadableStdio()); - const stream = subprocess.duplex(); - const secondStream = subprocess.writable({to: 'fd3'}); - - const expectedOutput = `${foobarString}${foobarString}`; - await Promise.all([ - assertStreamOutput(t, stream, expectedOutput), - finishedStream(secondStream), - endDifferentWritable(t, stream, secondStream, subprocess), - ]); - await assertSubprocessOutput(t, subprocess, expectedOutput); -}); - -test('Can call .readable() and .duplex()', async t => { - const subprocess = execa('stdin-both.js'); - const stream = subprocess.duplex(); - const secondStream = subprocess.readable({from: 'stderr'}); - stream.end(foobarString); - - await Promise.all([ - assertStreamOutput(t, stream), - assertStreamOutput(t, secondStream), - ]); - await assertSubprocessOutput(t, subprocess); - await assertSubprocessOutput(t, subprocess, foobarString, 2); -}); - -test('Can error one of two .readable() on same file descriptor', async t => { - const subprocess = execa('noop-fd.js', ['1', foobarString]); - const stream = subprocess.readable(); - const secondStream = subprocess.readable(); - const cause = new Error(foobarString); - stream.destroy(cause); - - await Promise.all([ - assertStreamReadError(t, stream, cause), - assertStreamOutput(t, secondStream), - ]); - await assertSubprocessOutput(t, subprocess); -}); - -test('Can error both .readable() on same file descriptor', async t => { - const subprocess = execa('noop-fd.js', ['1', foobarString]); - const stream = subprocess.readable(); - const secondStream = subprocess.readable(); - const cause = new Error(foobarString); - stream.destroy(cause); - secondStream.destroy(cause); - - const [error, secondError] = await Promise.all([ - assertStreamReadError(t, stream, {cause}), - assertStreamReadError(t, secondStream, {cause}), - ]); - t.is(error, secondError); - await assertSubprocessError(t, subprocess, error); -}); - -test('Can error one of two .readable() on different file descriptors', async t => { - const subprocess = execa('noop-both.js', [foobarString]); - const stream = subprocess.readable(); - const secondStream = subprocess.readable({from: 'stderr'}); - const cause = new Error(foobarString); - stream.destroy(cause); - - const [error, secondError] = await Promise.all([ - assertStreamReadError(t, stream, {cause}), - assertStreamReadError(t, secondStream, {cause}), - ]); - t.is(error, secondError); - t.is(error.stderr, foobarString); - await assertSubprocessError(t, subprocess, error); -}); - -test('Can error both .readable() on different file descriptors', async t => { - const subprocess = execa('noop-both.js', [foobarString]); - const stream = subprocess.readable(); - const secondStream = subprocess.readable({from: 'stderr'}); - const cause = new Error(foobarString); - stream.destroy(cause); - secondStream.destroy(cause); - - const [error, secondError] = await Promise.all([ - assertStreamReadError(t, stream, {cause}), - assertStreamReadError(t, secondStream, {cause}), - ]); - t.is(error, secondError); - await assertSubprocessError(t, subprocess, error); -}); - -test('Can error one of two .writable() on same file descriptor', async t => { - const subprocess = execa('stdin.js'); - const stream = subprocess.writable(); - const secondStream = subprocess.writable(); - const cause = new Error(foobarString); - stream.destroy(cause); - secondStream.end(foobarString); - - await Promise.all([ - assertStreamError(t, stream, cause), - finishedStream(secondStream), - ]); - await assertSubprocessOutput(t, subprocess); -}); - -test('Can error both .writable() on same file descriptor', async t => { - const subprocess = execa('stdin.js'); - const stream = subprocess.writable(); - const secondStream = subprocess.writable(); - const cause = new Error(foobarString); - stream.destroy(cause); - secondStream.destroy(cause); - - const [error, secondError] = await Promise.all([ - assertStreamError(t, stream, {cause}), - assertStreamError(t, secondStream, {cause}), - ]); - t.is(error, secondError); - await assertSubprocessError(t, subprocess, error); -}); - -test('Can error one of two .writable() on different file descriptors', async t => { - const subprocess = execa('stdin-fd-both.js', ['3'], fullReadableStdio()); - const stream = subprocess.writable(); - const secondStream = subprocess.writable({to: 'fd3'}); - const cause = new Error(foobarString); - stream.destroy(cause); - secondStream.end(foobarString); - - const [error, secondError] = await Promise.all([ - assertStreamError(t, stream, {cause}), - assertStreamError(t, secondStream, {cause}), - ]); - t.is(error, secondError); - t.is(error.stdout, foobarString); - await assertSubprocessError(t, subprocess, error); -}); - -test('Can error both .writable() on different file descriptors', async t => { - const subprocess = execa('stdin-fd-both.js', ['3'], fullReadableStdio()); - const stream = subprocess.writable(); - const secondStream = subprocess.writable({to: 'fd3'}); - const cause = new Error(foobarString); - stream.destroy(cause); - secondStream.destroy(cause); - - const [error, secondError] = await Promise.all([ - assertStreamError(t, stream, {cause}), - assertStreamError(t, secondStream, {cause}), - ]); - t.is(error, secondError); - await assertSubprocessError(t, subprocess, error); -}); - -test('Can error one of two .duplex() on same file descriptor', async t => { - const subprocess = execa('stdin.js'); - const stream = subprocess.duplex(); - const secondStream = subprocess.duplex(); - const cause = new Error(foobarString); - stream.destroy(cause); - secondStream.end(foobarString); - - await Promise.all([ - assertStreamReadError(t, stream, cause), - assertStreamOutput(t, secondStream), - ]); - await assertSubprocessOutput(t, subprocess); -}); - -test('Can error both .duplex() on same file descriptor', async t => { - const subprocess = execa('stdin.js'); - const stream = subprocess.duplex(); - const secondStream = subprocess.duplex(); - const cause = new Error(foobarString); - stream.destroy(cause); - secondStream.destroy(cause); - - await Promise.all([ - assertStreamReadError(t, stream, cause), - assertStreamReadError(t, secondStream, cause), - ]); - await assertSubprocessError(t, subprocess, {cause}); -}); - -test('Can error one of two .duplex() on different file descriptors', async t => { - const subprocess = execa('stdin-twice-both.js', ['3'], fullReadableStdio()); - const stream = subprocess.duplex(); - const secondStream = subprocess.duplex({from: 'stderr', to: 'fd3'}); - const cause = new Error(foobarString); - stream.destroy(cause); - secondStream.end(foobarString); - - const [error] = await Promise.all([ - assertStreamReadError(t, secondStream, {cause}), - assertStreamReadError(t, stream, cause), - ]); - t.is(error.stderr, foobarString); - await assertSubprocessError(t, subprocess, error); -}); - -test('Can error both .duplex() on different file descriptors', async t => { - const subprocess = execa('stdin-twice-both.js', ['3'], fullReadableStdio()); - const stream = subprocess.duplex(); - const secondStream = subprocess.duplex({from: 'stderr', to: 'fd3'}); - const cause = new Error(foobarString); - stream.destroy(cause); - secondStream.destroy(cause); - - await Promise.all([ - assertStreamReadError(t, stream, cause), - assertStreamReadError(t, secondStream, cause), - ]); - await assertSubprocessError(t, subprocess, {cause}); -}); diff --git a/test/convert/duplex.js b/test/convert/duplex.js deleted file mode 100644 index dd71ce371a..0000000000 --- a/test/convert/duplex.js +++ /dev/null @@ -1,195 +0,0 @@ -import { - compose, - Readable, - Writable, - PassThrough, -} from 'node:stream'; -import {pipeline} from 'node:stream/promises'; -import {text} from 'node:stream/consumers'; -import test from 'ava'; -import {execa} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import { - finishedStream, - assertReadableAborted, - assertWritableAborted, - assertProcessNormalExit, - assertStreamOutput, - assertStreamError, - assertStreamReadError, - assertSubprocessOutput, - assertSubprocessError, - assertPromiseError, - getReadWriteSubprocess, -} from '../helpers/convert.js'; -import {foobarString} from '../helpers/input.js'; -import {prematureClose, fullStdio, fullReadableStdio} from '../helpers/stdio.js'; -import {defaultHighWaterMark} from '../helpers/stream.js'; - -setFixtureDirectory(); - -test('.duplex() success', async t => { - const subprocess = getReadWriteSubprocess(); - const stream = subprocess.duplex(); - - t.true(stream instanceof Writable); - t.true(stream.writable); - t.true(stream instanceof Readable); - t.true(stream.readable); - - stream.end(foobarString); - - await assertStreamOutput(t, stream); - await assertSubprocessOutput(t, subprocess); -}); - -// eslint-disable-next-line max-params -const testReadableDuplexDefault = async (t, fdNumber, from, options, hasResult) => { - const subprocess = execa('noop-stdin-fd.js', [`${fdNumber}`], options); - const stream = subprocess.duplex({from}); - stream.end(foobarString); - - await assertStreamOutput(t, stream, hasResult ? foobarString : ''); - await assertSubprocessOutput(t, subprocess, foobarString, fdNumber); -}; - -test('.duplex() can use stdout', testReadableDuplexDefault, 1, 'stdout', {}, true); -test('.duplex() can use stderr', testReadableDuplexDefault, 2, 'stderr', {}, true); -test('.duplex() can use output stdio[*]', testReadableDuplexDefault, 3, 'fd3', fullStdio, true); -test('.duplex() uses stdout by default', testReadableDuplexDefault, 1, undefined, {}, true); -test('.duplex() does not use stderr by default', testReadableDuplexDefault, 2, undefined, {}, false); -test('.duplex() does not use stdio[*] by default', testReadableDuplexDefault, 3, undefined, fullStdio, false); -test('.duplex() uses stdout even if stderr is "ignore"', testReadableDuplexDefault, 1, 'stdout', {stderr: 'ignore'}, true); -test('.duplex() uses stderr even if stdout is "ignore"', testReadableDuplexDefault, 2, 'stderr', {stdout: 'ignore'}, true); -test('.duplex() uses stdout if "all" is used', testReadableDuplexDefault, 1, 'all', {all: true}, true); -test('.duplex() uses stderr if "all" is used', testReadableDuplexDefault, 2, 'all', {all: true}, true); - -const testWritableDuplexDefault = async (t, fdNumber, to, options) => { - const subprocess = execa('stdin-fd.js', [`${fdNumber}`], options); - const stream = subprocess.duplex({to}); - - stream.end(foobarString); - - await assertStreamOutput(t, stream, foobarString); - await assertSubprocessOutput(t, subprocess); -}; - -test('.duplex() can use stdin', testWritableDuplexDefault, 0, 'stdin', {}); -test('.duplex() can use input stdio[*]', testWritableDuplexDefault, 3, 'fd3', fullReadableStdio()); -test('.duplex() uses stdin by default', testWritableDuplexDefault, 0, undefined, {}); - -test('.duplex() abort -> subprocess fail', async t => { - const subprocess = getReadWriteSubprocess(); - const stream = subprocess.duplex(); - const textPromise = text(stream); - - stream.destroy(); - - const error = await t.throwsAsync(textPromise); - t.like(error, prematureClose); - assertProcessNormalExit(t, error); - assertWritableAborted(t, subprocess.stdin); - assertReadableAborted(t, subprocess.stdout); - t.true(subprocess.stderr.readableEnded); - await assertSubprocessError(t, subprocess, error); -}); - -test('.duplex() error -> subprocess fail', async t => { - const subprocess = getReadWriteSubprocess(); - const stream = subprocess.duplex(); - - const cause = new Error(foobarString); - stream.destroy(cause); - - const error = await assertStreamError(t, stream, {cause}); - assertProcessNormalExit(t, error); - t.is(subprocess.stdin.errored, cause); - t.is(subprocess.stdout.errored, cause); - t.true(subprocess.stderr.readableEnded); - await assertSubprocessError(t, subprocess, error); -}); - -test('.duplex() can be used with Stream.pipeline()', async t => { - const subprocess = getReadWriteSubprocess(); - const inputStream = Readable.from([foobarString]); - const stream = subprocess.duplex(); - const outputStream = new PassThrough(); - - await pipeline(inputStream, stream, outputStream); - - await finishedStream(inputStream); - await finishedStream(stream); - await assertStreamOutput(t, outputStream); - await assertSubprocessOutput(t, subprocess); -}); - -test('.duplex() can error with Stream.pipeline()', async t => { - const subprocess = execa('stdin-fail.js'); - const inputStream = Readable.from([foobarString]); - const stream = subprocess.duplex(); - const outputStream = new PassThrough(); - - const error = await t.throwsAsync(pipeline(inputStream, stream, outputStream)); - assertProcessNormalExit(t, error, 2); - t.like(error, {stdout: foobarString}); - - await finishedStream(inputStream); - await assertStreamError(t, stream, error); - await assertStreamReadError(t, outputStream, error); - await assertSubprocessError(t, subprocess, error); -}); - -test('.duplex() can pipe to errored stream with Stream.pipeline()', async t => { - const subprocess = execa('stdin-fail.js'); - const inputStream = Readable.from([foobarString]); - const stream = subprocess.duplex(); - const outputStream = new PassThrough(); - - const cause = new Error('test'); - outputStream.destroy(cause); - - await assertPromiseError(t, pipeline(inputStream, stream, outputStream), cause); - await t.throwsAsync(finishedStream(stream)); - - await assertStreamError(t, inputStream, cause); - const error = await assertStreamError(t, stream, cause); - await assertStreamReadError(t, outputStream, cause); - await assertSubprocessError(t, subprocess, {cause: error}); -}); - -test('.duplex() can be piped to errored stream with Stream.pipeline()', async t => { - const subprocess = execa('stdin-fail.js'); - const inputStream = Readable.from([foobarString]); - const stream = subprocess.duplex(); - const outputStream = new PassThrough(); - - const cause = new Error('test'); - inputStream.destroy(cause); - - await assertPromiseError(t, pipeline(inputStream, stream, outputStream), cause); - await t.throwsAsync(finishedStream(stream)); - - await assertStreamError(t, inputStream, cause); - const error = await assertStreamError(t, stream, cause); - await assertStreamReadError(t, outputStream, cause); - await assertSubprocessError(t, subprocess, {cause: error}); -}); - -test('.duplex() can be used with Stream.compose()', async t => { - const subprocess = getReadWriteSubprocess(); - const inputStream = Readable.from([foobarString]); - const stream = subprocess.duplex(); - const outputStream = new PassThrough(); - - await assertStreamOutput(t, compose(inputStream, stream, outputStream)); - await assertSubprocessOutput(t, subprocess); -}); - -test('.duplex() has the right highWaterMark', async t => { - const subprocess = getReadWriteSubprocess(); - const stream = subprocess.duplex(); - t.is(stream.readableHighWaterMark, defaultHighWaterMark); - t.is(stream.writableHighWaterMark, defaultHighWaterMark); - stream.end(); - await text(stream); -}); diff --git a/test/convert/iterable.js b/test/convert/iterable.js deleted file mode 100644 index 0f6dc25ff3..0000000000 --- a/test/convert/iterable.js +++ /dev/null @@ -1,154 +0,0 @@ -import test from 'ava'; -import {execa} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString} from '../helpers/input.js'; -import {fullStdio, assertEpipe} from '../helpers/stdio.js'; -import { - arrayFromAsync, - assertWritableAborted, - assertReadableAborted, - assertProcessNormalExit, -} from '../helpers/convert.js'; -import {simpleFull, noNewlinesChunks} from '../helpers/lines.js'; - -setFixtureDirectory(); - -const partialArrayFromAsync = async (asyncIterable, lines = []) => { - // eslint-disable-next-line no-unreachable-loop - for await (const line of asyncIterable) { - lines.push(line); - break; - } - - return lines; -}; - -const errorArrayFromAsync = async (t, cause, asyncIterable, lines = []) => { - const {value} = await asyncIterable.next(); - lines.push(value); - await asyncIterable.throw(cause); -}; - -const throwsAsync = async (t, asyncIterable, arrayFromAsyncMethod) => { - const lines = []; - const error = await t.throwsAsync(arrayFromAsyncMethod(asyncIterable, lines)); - return {error, lines}; -}; - -const assertStdoutAbort = (t, subprocess, error, cause) => { - assertProcessNormalExit(t, error, 1); - assertEpipe(t, error.stderr); - assertWritableAborted(t, subprocess.stdin); - t.true(subprocess.stderr.readableEnded); - - if (cause === undefined) { - assertReadableAborted(t, subprocess.stdout); - } else { - t.is(subprocess.stdout.errored, cause); - } -}; - -const testSuccess = async (t, fdNumber, from, options = {}) => { - const lines = await arrayFromAsync(execa('noop-fd.js', [`${fdNumber}`, simpleFull], options).iterable({from})); - t.deepEqual(lines, noNewlinesChunks); -}; - -test('Uses stdout by default', testSuccess, 1, undefined); -test('Can iterate successfully on stdout', testSuccess, 1, 'stdout'); -test('Can iterate successfully on stderr', testSuccess, 2, 'stderr'); -test('Can iterate successfully on stdio[*]', testSuccess, 3, 'fd3', fullStdio); - -test('Can iterate successfully on all', async t => { - const lines = await arrayFromAsync(execa('noop-both.js', [simpleFull], {all: true}).iterable({from: 'all'})); - t.deepEqual(lines, [...noNewlinesChunks, ...noNewlinesChunks]); -}); - -test('Can iterate using Symbol.asyncIterator', async t => { - const lines = await arrayFromAsync(execa('noop-fd.js', ['1', simpleFull])); - t.deepEqual(lines, noNewlinesChunks); -}); - -const assertMultipleCalls = async (t, iterable, iterableTwo) => { - t.not(iterable, iterableTwo); - const lines = await arrayFromAsync(iterable); - const linesTwo = await arrayFromAsync(iterableTwo); - t.deepEqual(lines, linesTwo); - t.deepEqual(lines, noNewlinesChunks); -}; - -test('Can be called multiple times', async t => { - const subprocess = execa('noop-fd.js', ['1', simpleFull]); - const iterable = subprocess.iterable(); - const iterableTwo = subprocess.iterable(); - await assertMultipleCalls(t, iterable, iterableTwo); -}); - -test('Can be called on different file descriptors', async t => { - const subprocess = execa('noop-both.js', [simpleFull]); - const iterable = subprocess.iterable(); - const iterableTwo = subprocess.iterable({from: 'stderr'}); - await assertMultipleCalls(t, iterable, iterableTwo); -}); - -test('Wait for the subprocess exit', async t => { - const subprocess = execa('noop-delay.js', ['1', simpleFull]); - const linesPromise = arrayFromAsync(subprocess); - t.is(await Promise.race([linesPromise, subprocess]), await subprocess); - t.deepEqual(await linesPromise, noNewlinesChunks); -}); - -test('Wait for the subprocess exit on iterator.return()', async t => { - const subprocess = execa('noop-delay.js', ['1', simpleFull]); - const linesPromise = partialArrayFromAsync(subprocess); - t.is(await Promise.race([linesPromise, subprocess]), await subprocess); - t.deepEqual(await linesPromise, [noNewlinesChunks[0]]); -}); - -test('Wait for the subprocess exit on iterator.throw()', async t => { - const subprocess = execa('noop-delay.js', ['1', simpleFull]); - const cause = new Error(foobarString); - const lines = []; - const linesPromise = t.throwsAsync(errorArrayFromAsync(t, cause, subprocess.iterable(), lines)); - t.is(await Promise.race([linesPromise, subprocess]), await subprocess); - t.deepEqual(lines, [noNewlinesChunks[0]]); -}); - -test('Abort stdout on iterator.return()', async t => { - const subprocess = execa('noop-repeat.js', ['1', simpleFull]); - const {error, lines} = await throwsAsync(t, subprocess, partialArrayFromAsync); - t.deepEqual(lines, [noNewlinesChunks[0]]); - assertStdoutAbort(t, subprocess, error); - t.is(error, await t.throwsAsync(subprocess)); -}); - -test('Abort stdout on iterator.throw()', async t => { - const subprocess = execa('noop-repeat.js', ['1', simpleFull]); - const cause = new Error(foobarString); - const {error, lines} = await throwsAsync(t, subprocess.iterable(), errorArrayFromAsync.bind(undefined, t, cause)); - t.deepEqual(lines, [noNewlinesChunks[0]]); - assertStdoutAbort(t, subprocess, error); - t.is(error, await t.throwsAsync(subprocess)); -}); - -test('Propagate subprocess failure', async t => { - const subprocess = execa('noop-fail.js', ['1', simpleFull]); - const {error, lines} = await throwsAsync(t, subprocess, arrayFromAsync); - t.is(error, await t.throwsAsync(subprocess)); - t.deepEqual(lines, noNewlinesChunks); -}); - -const testStdoutError = async (t, destroyStdout, isAbort, cause) => { - const subprocess = execa('noop-repeat.js', ['1', simpleFull]); - subprocess.stdout.once('data', () => { - destroyStdout(subprocess.stdout, cause); - }); - - const {error} = await throwsAsync(t, subprocess, arrayFromAsync); - t.is(error.cause, cause); - assertStdoutAbort(t, subprocess, error, isAbort ? undefined : cause); - t.is(error, await t.throwsAsync(subprocess)); -}; - -test('Propagate stdout abort', testStdoutError, subprocessStdout => subprocessStdout.destroy(), true); -test('Propagate stdout error', testStdoutError, (subprocessStdout, cause) => subprocessStdout.destroy(cause), false, new Error(foobarString)); -test('Propagate stdout "error" event', testStdoutError, (subprocessStdout, cause) => subprocessStdout.emit('error', cause), true, new Error(foobarString)); diff --git a/test/convert/readable.js b/test/convert/readable.js deleted file mode 100644 index 3b9454cb46..0000000000 --- a/test/convert/readable.js +++ /dev/null @@ -1,460 +0,0 @@ -import {once} from 'node:events'; -import process from 'node:process'; -import { - compose, - Readable, - Writable, - PassThrough, -} from 'node:stream'; -import {pipeline} from 'node:stream/promises'; -import {text} from 'node:stream/consumers'; -import {setTimeout} from 'node:timers/promises'; -import test from 'ava'; -import {execa} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import { - finishedStream, - assertReadableAborted, - assertWritableAborted, - assertProcessNormalExit, - assertStreamOutput, - assertStreamChunks, - assertStreamError, - assertStreamReadError, - assertSubprocessOutput, - assertSubprocessError, - assertPromiseError, - getReadableSubprocess, - getReadWriteSubprocess, -} from '../helpers/convert.js'; -import {foobarString, foobarBuffer, foobarObject} from '../helpers/input.js'; -import {simpleFull} from '../helpers/lines.js'; -import {prematureClose, fullStdio} from '../helpers/stdio.js'; -import {outputObjectGenerator, getOutputsAsyncGenerator} from '../helpers/generator.js'; -import {defaultHighWaterMark, defaultObjectHighWaterMark} from '../helpers/stream.js'; - -setFixtureDirectory(); - -test('.readable() success', async t => { - const subprocess = getReadableSubprocess(); - const stream = subprocess.readable(); - - t.false(stream instanceof Writable); - t.is(stream.writable, undefined); - t.true(stream instanceof Readable); - t.true(stream.readable); - - await assertStreamOutput(t, stream); - await assertSubprocessOutput(t, subprocess); -}); - -// eslint-disable-next-line max-params -const testReadableDefault = async (t, fdNumber, from, options, hasResult) => { - const subprocess = execa('noop-fd.js', [`${fdNumber}`, foobarString], options); - const stream = subprocess.readable({from}); - - await assertStreamOutput(t, stream, hasResult ? foobarString : ''); - await assertSubprocessOutput(t, subprocess, foobarString, fdNumber); -}; - -test('.readable() can use stdout', testReadableDefault, 1, 'stdout', {}, true); -test('.readable() can use stderr', testReadableDefault, 2, 'stderr', {}, true); -test('.readable() can use stdio[*]', testReadableDefault, 3, 'fd3', fullStdio, true); -test('.readable() uses stdout by default', testReadableDefault, 1, undefined, {}, true); -test('.readable() does not use stderr by default', testReadableDefault, 2, undefined, {}, false); -test('.readable() does not use stdio[*] by default', testReadableDefault, 3, undefined, fullStdio, false); -test('.readable() uses stdout even if stderr is "ignore"', testReadableDefault, 1, 'stdout', {stderr: 'ignore'}, true); -test('.readable() uses stderr even if stdout is "ignore"', testReadableDefault, 2, 'stderr', {stdout: 'ignore'}, true); -test('.readable() uses stdout if "all" is used', testReadableDefault, 1, 'all', {all: true}, true); -test('.readable() uses stderr if "all" is used', testReadableDefault, 2, 'all', {all: true}, true); - -const testBuffering = async (t, methodName) => { - const subprocess = execa('noop-stdin-fd.js', ['1'], {buffer: false}); - const stream = subprocess[methodName](); - - subprocess.stdin.write(foobarString); - await once(subprocess.stdout, 'readable'); - subprocess.stdin.end(); - - await assertStreamOutput(t, stream); -}; - -test('.readable() buffers until read', testBuffering, 'readable'); -test('.duplex() buffers until read', testBuffering, 'duplex'); - -test('.readable() abort -> subprocess fail', async t => { - const subprocess = execa('noop-repeat.js'); - const stream = subprocess.readable(); - - stream.destroy(); - - const error = await t.throwsAsync(text(stream)); - assertProcessNormalExit(t, error, 1); - t.true(error.message.includes('EPIPE')); - assertWritableAborted(t, subprocess.stdin); - assertReadableAborted(t, subprocess.stdout); - t.true(subprocess.stderr.readableEnded); - await assertSubprocessError(t, subprocess, error); -}); - -test('.readable() error -> subprocess fail', async t => { - const subprocess = execa('noop-repeat.js'); - const stream = subprocess.readable(); - - const cause = new Error(foobarString); - stream.destroy(cause); - - const error = await assertStreamReadError(t, stream, {cause}); - assertProcessNormalExit(t, error, 1); - t.true(error.message.includes('EPIPE')); - assertWritableAborted(t, subprocess.stdin); - t.is(subprocess.stdout.errored, cause); - t.true(subprocess.stderr.readableEnded); - await assertSubprocessError(t, subprocess, error); -}); - -const testStdoutAbort = async (t, methodName) => { - const subprocess = execa('ipc-echo.js', {ipc: true}); - const stream = subprocess[methodName](); - - subprocess.stdout.destroy(); - - await subprocess.sendMessage(foobarString); - const [error, message] = await Promise.all([ - t.throwsAsync(finishedStream(stream)), - subprocess.getOneMessage(), - ]); - t.like(error, prematureClose); - t.is(message, foobarString); - assertWritableAborted(t, subprocess.stdin); - assertReadableAborted(t, subprocess.stdout); - t.true(subprocess.stderr.readableEnded); - await assertSubprocessOutput(t, subprocess, ''); -}; - -test('subprocess.stdout abort + no more writes -> .readable() error + subprocess success', testStdoutAbort, 'readable'); -test('subprocess.stdout abort + no more writes -> .duplex() error + subprocess success', testStdoutAbort, 'duplex'); - -const testStdoutError = async (t, methodName) => { - const subprocess = execa('ipc-echo.js', {ipc: true}); - const stream = subprocess[methodName](); - - const cause = new Error(foobarString); - subprocess.stdout.destroy(cause); - - await subprocess.sendMessage(foobarString); - const [error, message] = await Promise.all([ - t.throwsAsync(finishedStream(stream)), - subprocess.getOneMessage(), - ]); - t.is(message, foobarString); - t.is(error.cause, cause); - assertProcessNormalExit(t, error); - t.is(subprocess.stdout.errored, cause); - t.true(subprocess.stderr.readableEnded); - assertWritableAborted(t, subprocess.stdin); - - await assertSubprocessError(t, subprocess, error); -}; - -test('subprocess.stdout error + no more writes -> .readable() error + subprocess fail', testStdoutError, 'readable'); -test('subprocess.stdout error + no more writes -> .duplex() error + subprocess fail', testStdoutError, 'duplex'); - -const testStdinAbortWrites = async (t, methodName) => { - const subprocess = getReadWriteSubprocess(); - const stream = subprocess[methodName](); - - subprocess.stdout.destroy(); - subprocess.stdin.end(foobarString); - - const error = await t.throwsAsync(finishedStream(stream)); - assertProcessNormalExit(t, error, 1); - t.true(subprocess.stdin.writableEnded); - assertReadableAborted(t, subprocess.stdout); - t.true(subprocess.stderr.readableEnded); - await assertSubprocessError(t, subprocess, error); -}; - -test('subprocess.stdout abort + more writes -> .readable() error + subprocess fail', testStdinAbortWrites, 'readable'); -test('subprocess.stdout abort + more writes -> .duplex() error + subprocess fail', testStdinAbortWrites, 'duplex'); - -const testStdinErrorWrites = async (t, methodName) => { - const subprocess = getReadWriteSubprocess(); - const stream = subprocess[methodName](); - - const cause = new Error(foobarString); - subprocess.stdout.destroy(cause); - subprocess.stdin.end(foobarString); - - const error = await assertStreamError(t, stream, {cause}); - assertProcessNormalExit(t, error, 1); - t.true(subprocess.stdin.writableEnded); - t.is(subprocess.stdout.errored, cause); - t.true(subprocess.stderr.readableEnded); - await assertSubprocessError(t, subprocess, error); -}; - -test('subprocess.stdout error + more writes -> .readable() error + subprocess fail', testStdinErrorWrites, 'readable'); -test('subprocess.stdout error + more writes -> .duplex() error + subprocess fail', testStdinErrorWrites, 'duplex'); - -test('.readable() can be used with Stream.pipeline()', async t => { - const subprocess = getReadableSubprocess(); - const stream = subprocess.readable(); - const outputStream = new PassThrough(); - - await pipeline(stream, outputStream); - - await finishedStream(stream); - await assertStreamOutput(t, outputStream); - await assertSubprocessOutput(t, subprocess); -}); - -test('.readable() can error with Stream.pipeline()', async t => { - const subprocess = execa('noop-fail.js', ['1', foobarString]); - const stream = subprocess.readable(); - const outputStream = new PassThrough(); - - const error = await t.throwsAsync(pipeline(stream, outputStream)); - assertProcessNormalExit(t, error, 2); - t.like(error, {stdout: foobarString}); - - await assertStreamError(t, stream, error); - await assertStreamReadError(t, outputStream, error); - await assertSubprocessError(t, subprocess, error); -}); - -test('.readable() can pipe to errored stream with Stream.pipeline()', async t => { - const subprocess = getReadableSubprocess(); - const stream = subprocess.readable(); - const outputStream = new PassThrough(); - - const cause = new Error('test'); - outputStream.destroy(cause); - - await assertPromiseError(t, pipeline(stream, outputStream), cause); - await t.throwsAsync(finishedStream(stream)); - - const error = await assertStreamError(t, stream, cause); - await assertStreamReadError(t, outputStream, cause); - await assertSubprocessError(t, subprocess, {cause: error}); -}); - -test('.readable() can be used with Stream.compose()', async t => { - const subprocess = getReadableSubprocess(); - const stream = subprocess.readable(); - const outputStream = new PassThrough(); - - await assertStreamOutput(t, compose(stream, outputStream)); - await assertSubprocessOutput(t, subprocess); -}); - -test('.readable() works with objectMode', async t => { - const subprocess = execa('noop.js', {stdout: outputObjectGenerator()}); - const stream = subprocess.readable(); - t.true(stream.readableObjectMode); - t.is(stream.readableHighWaterMark, defaultObjectHighWaterMark); - - await assertStreamChunks(t, stream, [foobarObject]); - await assertSubprocessOutput(t, subprocess, [foobarObject]); -}); - -test('.duplex() works with objectMode and reads', async t => { - const subprocess = getReadWriteSubprocess({stdout: outputObjectGenerator()}); - const stream = subprocess.duplex(); - t.true(stream.readableObjectMode); - t.is(stream.readableHighWaterMark, defaultObjectHighWaterMark); - t.false(stream.writableObjectMode); - t.is(stream.writableHighWaterMark, defaultHighWaterMark); - stream.end(foobarString); - - await assertStreamChunks(t, stream, [foobarObject]); - await assertSubprocessOutput(t, subprocess, [foobarObject]); -}); - -test('.readable() works with default encoding', async t => { - const subprocess = getReadableSubprocess(); - const stream = subprocess.readable(); - t.is(stream.readableEncoding, null); - - await assertStreamChunks(t, stream, [foobarBuffer]); - await assertSubprocessOutput(t, subprocess, foobarString); -}); - -test('.duplex() works with default encoding', async t => { - const subprocess = getReadWriteSubprocess(); - const stream = subprocess.duplex(); - t.is(stream.readableEncoding, null); - stream.end(foobarString); - - await assertStreamChunks(t, stream, [foobarBuffer]); - await assertSubprocessOutput(t, subprocess, foobarString); -}); - -test('.readable() works with encoding "utf8"', async t => { - const subprocess = getReadableSubprocess(); - subprocess.stdout.setEncoding('utf8'); - const stream = subprocess.readable(); - t.is(stream.readableEncoding, 'utf8'); - - await assertStreamChunks(t, stream, [foobarString]); - await assertSubprocessOutput(t, subprocess, foobarString); -}); - -test('.duplex() works with encoding "utf8"', async t => { - const subprocess = getReadWriteSubprocess(); - subprocess.stdout.setEncoding('utf8'); - const stream = subprocess.duplex(); - t.is(stream.readableEncoding, 'utf8'); - stream.end(foobarBuffer); - - await assertStreamChunks(t, stream, [foobarString]); - await assertSubprocessOutput(t, subprocess, foobarString); -}); - -test('.readable() has the right highWaterMark', async t => { - const subprocess = execa('noop.js'); - const stream = subprocess.readable(); - t.is(stream.readableHighWaterMark, defaultHighWaterMark); - await text(stream); -}); - -test('.readable() can iterate over lines', async t => { - const subprocess = execa('noop-fd.js', ['1', simpleFull]); - const lines = []; - for await (const line of subprocess.readable({binary: false, preserveNewlines: false})) { - lines.push(line); - } - - const expectedLines = ['aaa', 'bbb', 'ccc']; - t.deepEqual(lines, expectedLines); - await assertSubprocessOutput(t, subprocess, simpleFull); -}); - -test('.readable() can wait for data', async t => { - const subprocess = execa('noop.js', {stdout: getOutputsAsyncGenerator([foobarString, foobarString])(false, true)}); - const stream = subprocess.readable(); - - t.is(stream.read(), null); - await once(stream, 'readable'); - t.is(stream.read().toString(), foobarString); - t.is(stream.read(), null); - await once(stream, 'readable'); - t.is(stream.read().toString(), foobarString); - t.is(stream.read(), null); - await once(stream, 'readable'); - t.is(stream.read(), null); - - await finishedStream(stream); - await assertSubprocessOutput(t, subprocess, `${foobarString}${foobarString}`); -}); - -const testBufferData = async (t, methodName) => { - const chunk = '.'.repeat(defaultHighWaterMark).repeat(2); - const subprocess = getReadWriteSubprocess(); - const stream = subprocess[methodName](); - subprocess.stdin.end(chunk); - - await assertStreamOutput(t, stream, chunk); - await assertSubprocessOutput(t, subprocess, chunk); -}; - -test('.readable() can buffer data', testBufferData, 'readable'); -test('.duplex() can buffer data', testBufferData, 'duplex'); - -const assertDataEvents = async (t, stream, subprocess) => { - const [output] = await once(stream, 'data'); - t.is(output.toString(), foobarString); - - await finishedStream(stream); - await assertSubprocessOutput(t, subprocess); -}; - -test('.readable() can be read with "data" events', async t => { - const subprocess = getReadableSubprocess(); - const stream = subprocess.readable(); - - await assertDataEvents(t, stream, subprocess); -}); - -test('.duplex() can be read with "data" events', async t => { - const subprocess = getReadWriteSubprocess(); - const stream = subprocess.duplex(); - stream.end(foobarString); - - await assertDataEvents(t, stream, subprocess); -}); - -const assertPause = async (t, stream, subprocess) => { - const onceData = once(stream, 'data'); - stream.pause(); - - t.is(stream.readableLength, 0); - do { - // eslint-disable-next-line no-await-in-loop - await setTimeout(10); - } while (stream.readableLength === 0); - - t.false(await Promise.race([onceData, false])); - - stream.resume(); - const [output] = await onceData; - t.is(output.toString(), foobarString); - - await finishedStream(stream); - await assertSubprocessOutput(t, subprocess); -}; - -test('.readable() can be paused', async t => { - const subprocess = getReadableSubprocess(); - const stream = subprocess.readable(); - - await assertPause(t, stream, subprocess); -}); - -test('.duplex() can be paused', async t => { - const subprocess = getReadWriteSubprocess(); - const stream = subprocess.duplex(); - stream.end(foobarString); - - await assertPause(t, stream, subprocess); -}); - -// This feature does not work on Node 18. -// @todo: remove after dropping support for Node 18. -const majorVersion = Number(process.version.split('.')[0].slice(1)); -if (majorVersion >= 20) { - const testHighWaterMark = async (t, methodName) => { - const subprocess = execa('stdin.js'); - const stream = subprocess[methodName](); - - let count = 0; - const onPause = once(subprocess.stdout, 'pause'); - for (; !subprocess.stdout.isPaused(); count += 1) { - subprocess.stdin.write('.'); - // eslint-disable-next-line no-await-in-loop - await Promise.race([onPause, once(subprocess.stdout, 'data')]); - } - - const expectedCount = defaultObjectHighWaterMark + 1; - const expectedOutput = '.'.repeat(expectedCount); - t.is(count, expectedCount); - subprocess.stdin.end(); - await assertStreamOutput(t, stream, expectedOutput); - await assertSubprocessOutput(t, subprocess, expectedOutput); - }; - - test('.readable() pauses its buffering when too high', testHighWaterMark, 'readable'); - test('.duplex() pauses its buffering when too high', testHighWaterMark, 'duplex'); -} - -const testBigOutput = async (t, methodName) => { - const bigChunk = '.'.repeat(1e6); - const subprocess = execa('stdin.js', {input: bigChunk}); - const stream = subprocess[methodName](); - - await assertStreamOutput(t, stream, bigChunk); - await assertSubprocessOutput(t, subprocess, bigChunk); -}; - -test('.readable() with big output', testBigOutput, 'readable'); -test('.duplex() with big output', testBigOutput, 'duplex'); diff --git a/test/convert/shared.js b/test/convert/shared.js deleted file mode 100644 index ec1364224c..0000000000 --- a/test/convert/shared.js +++ /dev/null @@ -1,56 +0,0 @@ -import test from 'ava'; -import {execa} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import { - finishedStream, - assertWritableAborted, - assertStreamError, - assertSubprocessError, - getReadWriteSubprocess, -} from '../helpers/convert.js'; -import {foobarString} from '../helpers/input.js'; - -setFixtureDirectory(); - -const testSubprocessFail = async (t, methodName) => { - const subprocess = getReadWriteSubprocess(); - const stream = subprocess[methodName](); - - const cause = new Error(foobarString); - subprocess.kill(cause); - - const error = await assertStreamError(t, stream, {cause}); - assertWritableAborted(t, subprocess.stdin); - t.true(subprocess.stdout.readableEnded); - t.true(subprocess.stderr.readableEnded); - - await assertSubprocessError(t, subprocess, error); -}; - -test('subprocess fail -> .readable() error', testSubprocessFail, 'readable'); -test('subprocess fail -> .writable() error', testSubprocessFail, 'writable'); -test('subprocess fail -> .duplex() error', testSubprocessFail, 'duplex'); - -const testErrorEvent = async (t, methodName) => { - const subprocess = execa('empty.js'); - const stream = subprocess[methodName](); - t.is(stream.listenerCount('error'), 0); - stream.destroy(); - await t.throwsAsync(finishedStream(stream)); -}; - -test('.readable() requires listening to "error" event', testErrorEvent, 'readable'); -test('.writable() requires listening to "error" event', testErrorEvent, 'writable'); -test('.duplex() requires listening to "error" event', testErrorEvent, 'duplex'); - -const testSubprocessError = async (t, methodName) => { - const subprocess = getReadWriteSubprocess(); - const stream = subprocess[methodName](); - const cause = new Error(foobarString); - subprocess.kill(cause); - await assertStreamError(t, stream, {cause}); -}; - -test('Do not need to await subprocess with .readable()', testSubprocessError, 'readable'); -test('Do not need to await subprocess with .writable()', testSubprocessError, 'writable'); -test('Do not need to await subprocess with .duplex()', testSubprocessError, 'duplex'); diff --git a/test/convert/writable.js b/test/convert/writable.js deleted file mode 100644 index 8df45fbeb3..0000000000 --- a/test/convert/writable.js +++ /dev/null @@ -1,395 +0,0 @@ -import {once} from 'node:events'; -import {compose, Readable, Writable} from 'node:stream'; -import {pipeline} from 'node:stream/promises'; -import {text} from 'node:stream/consumers'; -import {setTimeout, scheduler} from 'node:timers/promises'; -import {promisify} from 'node:util'; -import test from 'ava'; -import {execa} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import { - finishedStream, - assertWritableAborted, - assertProcessNormalExit, - assertStreamOutput, - assertStreamError, - assertSubprocessOutput, - assertSubprocessError, - assertPromiseError, - getWritableSubprocess, - getReadableSubprocess, - getReadWriteSubprocess, -} from '../helpers/convert.js'; -import { - foobarString, - foobarBuffer, - foobarObject, - foobarObjectString, -} from '../helpers/input.js'; -import {prematureClose, fullReadableStdio} from '../helpers/stdio.js'; -import { - throwingGenerator, - serializeGenerator, - noopAsyncGenerator, -} from '../helpers/generator.js'; -import {defaultHighWaterMark, defaultObjectHighWaterMark} from '../helpers/stream.js'; - -setFixtureDirectory(); - -test('.writable() success', async t => { - const subprocess = getWritableSubprocess(); - const stream = subprocess.writable(); - - t.true(stream instanceof Writable); - t.true(stream.writable); - t.false(stream instanceof Readable); - t.is(stream.readable, undefined); - - stream.end(foobarString); - - await finishedStream(stream); - await assertSubprocessOutput(t, subprocess, foobarString, 2); -}); - -const testWritableDefault = async (t, fdNumber, to, options) => { - const subprocess = execa('stdin-fd.js', [`${fdNumber}`], options); - const stream = subprocess.writable({to}); - - stream.end(foobarString); - - await finishedStream(stream); - await assertSubprocessOutput(t, subprocess); -}; - -test('.writable() can use stdin', testWritableDefault, 0, 'stdin', {}); -test('.writable() can use stdio[*]', testWritableDefault, 3, 'fd3', fullReadableStdio()); -test('.writable() uses stdin by default', testWritableDefault, 0, undefined, {}); - -test('.writable() hangs until ended', async t => { - const subprocess = getReadWriteSubprocess(); - const stream = subprocess.writable(); - - stream.write(foobarString); - await setTimeout(1e2); - stream.end(); - - await finishedStream(stream); - await assertSubprocessOutput(t, subprocess); -}); - -test('.duplex() hangs until ended', async t => { - const subprocess = getReadWriteSubprocess(); - const stream = subprocess.duplex(); - - stream.write(foobarString); - await setTimeout(1e2); - stream.end(); - - await assertStreamOutput(t, stream); - await assertSubprocessOutput(t, subprocess); -}); - -const testEarlySuccess = async (t, methodName, hasWrites) => { - const subprocess = hasWrites ? getReadableSubprocess() : execa('empty.js'); - const stream = subprocess[methodName](); - - const error = await t.throwsAsync(finishedStream(stream)); - t.like(error, prematureClose); - assertWritableAborted(t, subprocess.stdin); - t.true(subprocess.stdout.readableEnded); - t.true(subprocess.stderr.readableEnded); - await assertSubprocessOutput(t, subprocess, hasWrites ? foobarString : ''); -}; - -test('subprocess early success with no writes -> .writable() abort', testEarlySuccess, 'writable', false); -test('subprocess early success with no writes -> .duplex() abort', testEarlySuccess, 'duplex', false); -test('subprocess early success with writes -> .writable() abort', testEarlySuccess, 'writable', true); -test('subprocess early success with writes -> .duplex() abort', testEarlySuccess, 'duplex', true); - -test('.writable() abort -> subprocess fail', async t => { - const subprocess = getWritableSubprocess(); - const stream = subprocess.writable(); - - stream.destroy(); - - const error = await t.throwsAsync(finishedStream(stream)); - t.like(error, prematureClose); - assertProcessNormalExit(t, error); - assertWritableAborted(t, subprocess.stdin); - t.true(subprocess.stdout.readableEnded); - t.true(subprocess.stderr.readableEnded); - await assertSubprocessError(t, subprocess, error); -}); - -test('.writable() error -> subprocess fail', async t => { - const subprocess = getWritableSubprocess(); - const stream = subprocess.writable(); - - const cause = new Error(foobarString); - stream.destroy(cause); - - const error = await assertStreamError(t, stream, {cause}); - assertProcessNormalExit(t, error); - t.is(subprocess.stdin.errored, cause); - t.true(subprocess.stdout.readableEnded); - t.true(subprocess.stderr.readableEnded); - await assertSubprocessError(t, subprocess, error); -}); - -test('.writable() EPIPE error -> subprocess success', async t => { - const subprocess = getWritableSubprocess(); - const stream = subprocess.writable(); - - const error = new Error(foobarString); - error.code = 'EPIPE'; - stream.destroy(error); - - await assertStreamError(t, stream, error); - t.is(subprocess.stdin.errored, error); - t.true(subprocess.stdout.readableEnded); - t.true(subprocess.stderr.readableEnded); - await subprocess; -}); - -test('subprocess.stdin end -> .writable() end + subprocess success', async t => { - const subprocess = getReadWriteSubprocess(); - const stream = subprocess.writable(); - - subprocess.stdin.end(foobarString); - - await finishedStream(stream); - await assertSubprocessOutput(t, subprocess); -}); - -test('subprocess.stdin end -> .duplex() end + subprocess success', async t => { - const subprocess = getReadWriteSubprocess(); - const stream = subprocess.duplex(); - - subprocess.stdin.end(foobarString); - - await assertStreamOutput(t, stream); - await assertSubprocessOutput(t, subprocess); -}); - -const testStdinAbort = async (t, methodName) => { - const subprocess = getReadWriteSubprocess(); - const stream = subprocess[methodName](); - - subprocess.stdin.destroy(); - - const error = await t.throwsAsync(finishedStream(stream)); - t.like(error, prematureClose); - assertProcessNormalExit(t, error); - assertWritableAborted(t, subprocess.stdin); - t.true(subprocess.stdout.readableEnded); - t.true(subprocess.stderr.readableEnded); - await assertSubprocessError(t, subprocess, error); -}; - -test('subprocess.stdin abort -> .writable() error + subprocess fail', testStdinAbort, 'writable'); -test('subprocess.stdin abort -> .duplex() error + subprocess fail', testStdinAbort, 'duplex'); - -const testStdinError = async (t, methodName) => { - const subprocess = getReadWriteSubprocess(); - const stream = subprocess[methodName](); - - const cause = new Error(foobarString); - subprocess.stdin.destroy(cause); - - const error = await assertStreamError(t, stream, {cause}); - assertProcessNormalExit(t, error); - t.is(subprocess.stdin.errored, cause); - t.true(subprocess.stderr.readableEnded); - t.true(subprocess.stdout.readableEnded); - await assertSubprocessError(t, subprocess, error); -}; - -test('subprocess.stdin error -> .writable() error + subprocess fail', testStdinError, 'writable'); -test('subprocess.stdin error -> .duplex() error + subprocess fail', testStdinError, 'duplex'); - -test('.writable() can be used with Stream.pipeline()', async t => { - const subprocess = getWritableSubprocess(); - const inputStream = Readable.from([foobarString]); - const stream = subprocess.writable(); - - await pipeline(inputStream, stream); - - await finishedStream(inputStream); - await finishedStream(stream); - await assertSubprocessOutput(t, subprocess, foobarString, 2); -}); - -test('.writable() can error with Stream.pipeline()', async t => { - const subprocess = execa('noop-stdin-fail.js', ['2']); - const inputStream = Readable.from([foobarString]); - const stream = subprocess.writable(); - - const error = await t.throwsAsync(pipeline(inputStream, stream)); - assertProcessNormalExit(t, error, 2); - t.is(error.stderr, foobarString); - - await finishedStream(inputStream); - await assertStreamError(t, stream, error); - await assertSubprocessError(t, subprocess, error); -}); - -test('.writable() can pipe to errored stream with Stream.pipeline()', async t => { - const subprocess = getWritableSubprocess(); - const inputStream = Readable.from([foobarString]); - const stream = subprocess.writable(); - - const cause = new Error('test'); - inputStream.destroy(cause); - - await assertPromiseError(t, pipeline(inputStream, stream), cause); - await t.throwsAsync(finishedStream(stream)); - - await assertStreamError(t, inputStream, cause); - const error = await assertStreamError(t, stream, cause); - await assertSubprocessError(t, subprocess, {cause: error}); -}); - -test('.writable() can be used with Stream.compose()', async t => { - const subprocess = getWritableSubprocess(); - const inputStream = Readable.from([foobarString]); - const stream = subprocess.writable(); - - await finishedStream(compose(inputStream, stream)); - await assertSubprocessOutput(t, subprocess, foobarString, 2); -}); - -test('.writable() works with objectMode', async t => { - const subprocess = getReadWriteSubprocess({stdin: serializeGenerator(true, true)}); - const stream = subprocess.writable(); - t.true(stream.writableObjectMode); - t.is(stream.writableHighWaterMark, defaultObjectHighWaterMark); - stream.end(foobarObject); - - await finishedStream(stream); - await assertSubprocessOutput(t, subprocess, foobarObjectString); -}); - -test('.duplex() works with objectMode and writes', async t => { - const subprocess = getReadWriteSubprocess({stdin: serializeGenerator(true, true)}); - const stream = subprocess.duplex(); - t.false(stream.readableObjectMode); - t.is(stream.readableHighWaterMark, defaultHighWaterMark); - t.true(stream.writableObjectMode); - t.is(stream.writableHighWaterMark, defaultObjectHighWaterMark); - stream.end(foobarObject); - - await assertStreamOutput(t, stream, foobarObjectString); - await assertSubprocessOutput(t, subprocess, foobarObjectString); -}); - -test('.writable() has the right highWaterMark', async t => { - const subprocess = getReadWriteSubprocess(); - const stream = subprocess.writable(); - t.is(stream.writableHighWaterMark, defaultHighWaterMark); - stream.end(); - await finishedStream(stream); -}); - -const writeUntilFull = async (t, stream, subprocess) => { - const size = stream.writableHighWaterMark / 2; - const chunk = '.'.repeat(size); - - t.is(subprocess.stdin.writableLength, 0); - t.is(stream.writableLength, 0); - t.false(subprocess.stdin.writableNeedDrain); - t.false(stream.writableNeedDrain); - - t.true(stream.write(chunk)); - t.is(subprocess.stdin.writableLength, size); - t.is(stream.writableLength, 0); - t.false(subprocess.stdin.writableNeedDrain); - t.false(stream.writableNeedDrain); - - t.true(stream.write(chunk)); - t.is(subprocess.stdin.writableLength, size * 2); - t.is(stream.writableLength, size); - t.true(subprocess.stdin.writableNeedDrain); - t.false(stream.writableNeedDrain); - - t.false(stream.write(chunk)); - t.is(subprocess.stdin.writableLength, size * 2); - t.is(stream.writableLength, size * 2); - t.true(subprocess.stdin.writableNeedDrain); - t.true(stream.writableNeedDrain); - - await once(stream, 'drain'); - stream.end(); - - return '.'.repeat(size * 3); -}; - -test('.writable() waits when its buffer is full', async t => { - const subprocess = getReadWriteSubprocess({stdin: noopAsyncGenerator(false, true)}); - const stream = subprocess.writable(); - - const expectedOutput = await writeUntilFull(t, stream, subprocess); - - await assertSubprocessOutput(t, subprocess, expectedOutput); -}); - -test('.duplex() waits when its buffer is full', async t => { - const subprocess = getReadWriteSubprocess({stdin: noopAsyncGenerator(false, true)}); - const stream = subprocess.duplex(); - - const expectedOutput = await writeUntilFull(t, stream, subprocess); - - await assertStreamOutput(t, stream, expectedOutput); - await assertSubprocessOutput(t, subprocess, expectedOutput); -}); - -const testPropagateError = async (t, methodName) => { - const cause = new Error(foobarString); - const subprocess = getReadWriteSubprocess({stdin: throwingGenerator(cause)()}); - const stream = subprocess[methodName](); - stream.end('.'); - await assertStreamError(t, stream, {cause}); -}; - -test('.writable() propagates write errors', testPropagateError, 'writable'); -test('.duplex() propagates write errors', testPropagateError, 'duplex'); - -const testWritev = async (t, methodName, waitForStream) => { - const subprocess = getReadWriteSubprocess({stdin: noopAsyncGenerator()}); - const stream = subprocess[methodName](); - - const chunk = '.'.repeat(stream.writableHighWaterMark); - stream.write(chunk); - t.true(stream.writableNeedDrain); - - const [writeInOneTick] = await Promise.race([ - Promise.all([true, promisify(stream.write.bind(stream))(chunk)]), - Promise.all([false, scheduler.yield()]), - ]); - t.true(writeInOneTick); - - stream.end(); - await waitForStream(stream); -}; - -test('.writable() can use .writev()', testWritev, 'writable', finishedStream); -test('.duplex() can use .writev()', testWritev, 'duplex', text); - -test('.writable() can set encoding', async t => { - const subprocess = getReadWriteSubprocess(); - const stream = subprocess.writable(); - - stream.end(foobarBuffer.toString('hex'), 'hex'); - - await finishedStream(stream); - await assertSubprocessOutput(t, subprocess); -}); - -test('.duplex() can set encoding', async t => { - const subprocess = getReadWriteSubprocess(); - const stream = subprocess.duplex(); - - stream.end(foobarBuffer.toString('hex'), 'hex'); - - await assertStreamOutput(t, stream); - await assertSubprocessOutput(t, subprocess); -}); diff --git a/test/io/input-option.js b/test/io/input-option.js deleted file mode 100644 index 1aa3726353..0000000000 --- a/test/io/input-option.js +++ /dev/null @@ -1,54 +0,0 @@ -import {Writable} from 'node:stream'; -import test from 'ava'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import { - runExeca, - runExecaSync, - runScript, - runScriptSync, -} from '../helpers/run.js'; -import { - foobarUint8Array, - foobarBuffer, - foobarArrayBuffer, - foobarUint16Array, - foobarDataView, -} from '../helpers/input.js'; - -setFixtureDirectory(); - -const testInput = async (t, input, execaMethod) => { - const {stdout} = await execaMethod('stdin.js', {input}); - t.is(stdout, 'foobar'); -}; - -test('input option can be a String', testInput, 'foobar', runExeca); -test('input option can be a Uint8Array', testInput, foobarUint8Array, runExeca); -test('input option can be a Buffer', testInput, foobarBuffer, runExeca); -test('input option can be a String - sync', testInput, 'foobar', runExecaSync); -test('input option can be a Uint8Array - sync', testInput, foobarUint8Array, runExecaSync); -test('input option can be a Buffer - sync', testInput, foobarBuffer, runExecaSync); -test('input option can be used with $', testInput, 'foobar', runScript); -test('input option can be used with $.sync', testInput, 'foobar', runScriptSync); - -const testInvalidInput = async (t, input, execaMethod) => { - t.throws(() => { - execaMethod('empty.js', {input}); - }, {message: /a string, a Uint8Array/}); -}; - -test('input option cannot be an ArrayBuffer', testInvalidInput, foobarArrayBuffer, execa); -test('input option cannot be a DataView', testInvalidInput, foobarDataView, execa); -test('input option cannot be a Uint16Array', testInvalidInput, foobarUint16Array, execa); -test('input option cannot be 0', testInvalidInput, 0, execa); -test('input option cannot be false', testInvalidInput, false, execa); -test('input option cannot be null', testInvalidInput, null, execa); -test('input option cannot be a non-Readable stream', testInvalidInput, new Writable(), execa); -test('input option cannot be an ArrayBuffer - sync', testInvalidInput, foobarArrayBuffer, execaSync); -test('input option cannot be a DataView - sync', testInvalidInput, foobarDataView, execaSync); -test('input option cannot be a Uint16Array - sync', testInvalidInput, foobarUint16Array, execaSync); -test('input option cannot be 0 - sync', testInvalidInput, 0, execaSync); -test('input option cannot be false - sync', testInvalidInput, false, execaSync); -test('input option cannot be null - sync', testInvalidInput, null, execaSync); -test('input option cannot be a non-Readable stream - sync', testInvalidInput, new Writable(), execaSync); diff --git a/test/io/input-sync.js b/test/io/input-sync.js deleted file mode 100644 index e656c94ebd..0000000000 --- a/test/io/input-sync.js +++ /dev/null @@ -1,18 +0,0 @@ -import test from 'ava'; -import {execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {getStdio} from '../helpers/stdio.js'; - -setFixtureDirectory(); - -const getFd3InputMessage = type => `not \`stdio[3]\`, can be ${type}`; - -const testFd3InputSync = (t, stdioOption, expectedMessage) => { - const {message} = t.throws(() => { - execaSync('empty.js', getStdio(3, stdioOption)); - }); - t.true(message.includes(expectedMessage)); -}; - -test('Cannot use Uint8Array with stdio[*], sync', testFd3InputSync, new Uint8Array(), getFd3InputMessage('a Uint8Array')); -test('Cannot use iterable with stdio[*], sync', testFd3InputSync, [[]], getFd3InputMessage('an iterable')); diff --git a/test/io/iterate.js b/test/io/iterate.js deleted file mode 100644 index 7a2620642f..0000000000 --- a/test/io/iterate.js +++ /dev/null @@ -1,206 +0,0 @@ -import {once} from 'node:events'; -import {getDefaultHighWaterMark} from 'node:stream'; -import test from 'ava'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import { - assertStreamOutput, - assertIterableChunks, - assertStreamChunks, - assertSubprocessOutput, - getReadableSubprocess, - getReadWriteSubprocess, -} from '../helpers/convert.js'; -import { - stringToUint8Arrays, - simpleFull, - simpleChunks, - simpleChunksBuffer, - simpleChunksUint8Array, - simpleLines, - noNewlinesFull, - complexFull, - complexFullUtf16, - complexFullUtf16Uint8Array, - singleComplexBuffer, - singleComplexUtf16Buffer, - singleComplexUint8Array, - singleComplexHex, - complexChunks, - complexChunksEnd, -} from '../helpers/lines.js'; -import {outputObjectGenerator, getOutputGenerator} from '../helpers/generator.js'; -import {foobarString, foobarObject} from '../helpers/input.js'; -import { - multibyteChar, - multibyteUint8Array, - breakingLength, - brokenSymbol, -} from '../helpers/encoding.js'; - -setFixtureDirectory(); - -const foobarObjectChunks = [foobarObject, foobarObject, foobarObject]; - -const getSubprocess = (methodName, output, options) => { - if (methodName !== 'duplex') { - return getReadableSubprocess(output, options); - } - - const subprocess = getReadWriteSubprocess(options); - subprocess.stdin.end(output); - return subprocess; -}; - -const assertChunks = async (t, streamOrIterable, expectedChunks, methodName) => { - const assertMethod = methodName === 'iterable' ? assertIterableChunks : assertStreamChunks; - await assertMethod(t, streamOrIterable, expectedChunks); -}; - -// eslint-disable-next-line max-params -const testText = async (t, expectedChunks, methodName, binary, preserveNewlines, encoding) => { - const subprocess = getReadWriteSubprocess({encoding}); - const input = encoding === 'utf16le' ? complexFullUtf16 : complexFull; - subprocess.stdin.end(input); - const stream = subprocess[methodName]({binary, preserveNewlines}); - - await assertChunks(t, stream, expectedChunks, methodName); - const expectedOutput = encoding === 'hex' - ? singleComplexHex - : stringToUint8Arrays(complexFull, encoding === 'buffer'); - await assertSubprocessOutput(t, subprocess, expectedOutput); -}; - -test('.iterable() can use "binary: true"', testText, [singleComplexUint8Array], 'iterable', true, undefined, 'utf8'); -test('.iterable() can use "binary: true" + "encoding: utf16le"', testText, [complexFullUtf16Uint8Array], 'iterable', true, undefined, 'utf16le'); -test('.iterable() can use "binary: true" + "encoding: "buffer"', testText, [singleComplexUint8Array], 'iterable', true, undefined, 'buffer'); -test('.iterable() can use "binary: true" + "encoding: "hex"', testText, [singleComplexUint8Array], 'iterable', true, undefined, 'hex'); -test('.iterable() can use "binary: undefined"', testText, complexChunks, 'iterable', undefined, undefined, 'utf8'); -test('.iterable() can use "binary: undefined" + "encoding: utf16le"', testText, complexChunks, 'iterable', undefined, undefined, 'utf16le'); -test('.iterable() can use "binary: undefined" + "encoding: buffer"', testText, [singleComplexUint8Array], 'iterable', undefined, undefined, 'buffer'); -test('.iterable() can use "binary: undefined" + "encoding: hex"', testText, [singleComplexUint8Array], 'iterable', undefined, undefined, 'hex'); -test('.iterable() can use "binary: false"', testText, complexChunks, 'iterable', false, undefined, 'utf8'); -test('.iterable() can use "binary: false" + "encoding: utf16le"', testText, complexChunks, 'iterable', false, undefined, 'utf16le'); -test('.iterable() can use "binary: false" + "encoding: buffer"', testText, [singleComplexUint8Array], 'iterable', false, undefined, 'buffer'); -test('.iterable() can use "binary: false" + "encoding: hex"', testText, [singleComplexUint8Array], 'iterable', false, undefined, 'hex'); -test('.iterable() can use "binary: false" + "preserveNewlines: true"', testText, complexChunksEnd, 'iterable', false, true, 'utf8'); -test('.iterable() can use "binary: false" + "preserveNewlines: false"', testText, complexChunks, 'iterable', false, false, 'utf8'); -test('.readable() can use "binary: true"', testText, singleComplexBuffer, 'readable', true, undefined, 'utf8'); -test('.readable() can use "binary: true" + "encoding: utf16le"', testText, singleComplexUtf16Buffer, 'readable', true, undefined, 'utf16le'); -test('.readable() can use "binary: true" + "encoding: buffer"', testText, singleComplexBuffer, 'readable', true, undefined, 'buffer'); -test('.readable() can use "binary: true" + "encoding: hex"', testText, singleComplexBuffer, 'readable', true, undefined, 'hex'); -test('.readable() can use "binary: undefined"', testText, singleComplexBuffer, 'readable', undefined, undefined, 'utf8'); -test('.readable() can use "binary: undefined" + "encoding: utf16le"', testText, singleComplexUtf16Buffer, 'readable', undefined, undefined, 'utf16le'); -test('.readable() can use "binary: undefined" + "encoding: buffer"', testText, singleComplexBuffer, 'readable', undefined, undefined, 'buffer'); -test('.readable() can use "binary: undefined" + "encoding: hex"', testText, singleComplexBuffer, 'readable', undefined, undefined, 'hex'); -test('.readable() can use "binary: false"', testText, complexChunksEnd, 'readable', false, undefined, 'utf8'); -test('.readable() can use "binary: false" + "encoding: utf16le"', testText, complexChunksEnd, 'readable', false, undefined, 'utf16le'); -test('.readable() can use "binary: false" + "encoding: buffer"', testText, singleComplexBuffer, 'readable', false, undefined, 'buffer'); -test('.readable() can use "binary: false" + "encoding: hex"', testText, singleComplexBuffer, 'readable', false, undefined, 'hex'); -test('.readable() can use "binary: false" + "preserveNewlines: true"', testText, complexChunksEnd, 'readable', false, true, 'utf8'); -test('.readable() can use "binary: false" + "preserveNewlines: false"', testText, complexChunks, 'readable', false, false, 'utf8'); -test('.duplex() can use "binary: true"', testText, singleComplexBuffer, 'duplex', true, undefined, 'utf8'); -test('.duplex() can use "binary: true" + "encoding: utf16le"', testText, singleComplexUtf16Buffer, 'duplex', true, undefined, 'utf16le'); -test('.duplex() can use "binary: true" + "encoding: buffer"', testText, singleComplexBuffer, 'duplex', true, undefined, 'buffer'); -test('.duplex() can use "binary: true" + "encoding: hex"', testText, singleComplexBuffer, 'duplex', true, undefined, 'hex'); -test('.duplex() can use "binary: undefined"', testText, singleComplexBuffer, 'duplex', undefined, undefined, 'utf8'); -test('.duplex() can use "binary: undefined" + "encoding: utf16le"', testText, singleComplexUtf16Buffer, 'duplex', undefined, undefined, 'utf16le'); -test('.duplex() can use "binary: undefined" + "encoding: "buffer"', testText, singleComplexBuffer, 'duplex', undefined, undefined, 'buffer'); -test('.duplex() can use "binary: undefined" + "encoding: "hex"', testText, singleComplexBuffer, 'duplex', undefined, undefined, 'hex'); -test('.duplex() can use "binary: false"', testText, complexChunksEnd, 'duplex', false, undefined, 'utf8'); -test('.duplex() can use "binary: false" + "encoding: utf16le"', testText, complexChunksEnd, 'duplex', false, undefined, 'utf16le'); -test('.duplex() can use "binary: false" + "encoding: buffer"', testText, singleComplexBuffer, 'duplex', false, undefined, 'buffer'); -test('.duplex() can use "binary: false" + "encoding: hex"', testText, singleComplexBuffer, 'duplex', false, undefined, 'hex'); -test('.duplex() can use "binary: false" + "preserveNewlines: true"', testText, complexChunksEnd, 'duplex', false, true, 'utf8'); -test('.duplex() can use "binary: false" + "preserveNewlines: false"', testText, complexChunks, 'duplex', false, false, 'utf8'); - -const testTextOutput = async (t, expectedOutput, methodName, preserveNewlines) => { - const subprocess = getSubprocess(methodName, complexFull); - const stream = subprocess[methodName]({binary: false, preserveNewlines}); - - await assertStreamOutput(t, stream, expectedOutput); - await assertSubprocessOutput(t, subprocess, complexFull); -}; - -test('.readable() "binary: false" keeps output as is', testTextOutput, complexFull, 'readable', undefined); -test('.readable() "binary: false" + "preserveNewlines: true" keeps output as is', testTextOutput, complexFull, 'readable', true); -test('.readable() "binary: false" + "preserveNewlines: false" removes all newlines', testTextOutput, noNewlinesFull, 'readable', false); -test('.duplex() "binary: false" keeps output as is', testTextOutput, complexFull, 'duplex', undefined); -test('.duplex() "binary: false" + "preserveNewlines: true" keeps output as is', testTextOutput, complexFull, 'duplex', true); -test('.duplex() "binary: false" + "preserveNewlines: false" removes all newlines', testTextOutput, noNewlinesFull, 'duplex', false); - -// eslint-disable-next-line max-params -const testObjectMode = async (t, expectedChunks, methodName, encoding, initialObjectMode, finalObjectMode, binary, options) => { - const subprocess = getSubprocess(methodName, simpleFull, options); - if (encoding !== null) { - subprocess.stdout.setEncoding(encoding); - } - - t.is(subprocess.stdout.readableEncoding, encoding); - t.is(subprocess.stdout.readableObjectMode, initialObjectMode); - t.is(subprocess.stdout.readableHighWaterMark, getDefaultHighWaterMark(initialObjectMode)); - - const stream = subprocess[methodName]({binary, preserveNewlines: true}); - - if (methodName !== 'iterable') { - t.is(stream.readableEncoding, encoding); - t.is(stream.readableObjectMode, finalObjectMode); - t.is(stream.readableHighWaterMark, getDefaultHighWaterMark(finalObjectMode)); - } - - t.is(subprocess.stdout.readableEncoding, encoding); - t.is(subprocess.stdout.readableObjectMode, initialObjectMode); - t.is(subprocess.stdout.readableHighWaterMark, getDefaultHighWaterMark(initialObjectMode)); - - await assertChunks(t, stream, expectedChunks, methodName); - await subprocess; -}; - -test('.iterable() uses Uint8Arrays with "binary: true"', testObjectMode, simpleChunksUint8Array, 'iterable', null, false, false, true); -test('.iterable() uses Uint8Arrays with "binary: true" and .setEncoding("utf8")', testObjectMode, simpleChunksUint8Array, 'iterable', 'utf8', false, false, true); -test('.iterable() uses Uint8Arrays with "binary: true", .setEncoding("utf8") and "encoding: buffer"', testObjectMode, simpleChunksUint8Array, 'iterable', 'utf8', false, false, true, {encoding: 'buffer'}); -test('.iterable() uses strings in objectMode with "binary: true" and object transforms', testObjectMode, foobarObjectChunks, 'iterable', null, true, true, true, {stdout: outputObjectGenerator()}); -test('.iterable() uses strings in objectMode with "binary: false"', testObjectMode, simpleLines, 'iterable', null, false, true, false); -test('.iterable() uses strings in objectMode with "binary: false" and .setEncoding("utf8")', testObjectMode, simpleLines, 'iterable', 'utf8', false, true, false); -test('.iterable() uses Uint8Arrays in objectMode with "binary: false", .setEncoding("utf8") and "encoding: buffer"', testObjectMode, simpleChunksUint8Array, 'iterable', 'utf8', false, true, false, {encoding: 'buffer'}); -test('.iterable() uses strings in objectMode with "binary: false" and object transforms', testObjectMode, foobarObjectChunks, 'iterable', null, true, true, false, {stdout: outputObjectGenerator()}); -test('.readable() uses Buffers with "binary: true"', testObjectMode, simpleChunksBuffer, 'readable', null, false, false, true); -test('.readable() uses strings with "binary: true" and .setEncoding("utf8")', testObjectMode, simpleChunks, 'readable', 'utf8', false, false, true); -test('.readable() uses strings with "binary: true", .setEncoding("utf8") and "encoding: buffer"', testObjectMode, simpleChunks, 'readable', 'utf8', false, false, true, {encoding: 'buffer'}); -test('.readable() uses strings in objectMode with "binary: true" and object transforms', testObjectMode, foobarObjectChunks, 'readable', null, true, true, true, {stdout: outputObjectGenerator()}); -test('.readable() uses strings in objectMode with "binary: false"', testObjectMode, simpleLines, 'readable', null, false, true, false); -test('.readable() uses strings in objectMode with "binary: false" and .setEncoding("utf8")', testObjectMode, simpleLines, 'readable', 'utf8', false, true, false); -test('.readable() uses strings in objectMode with "binary: false", .setEncoding("utf8") and "encoding: buffer"', testObjectMode, simpleChunks, 'readable', 'utf8', false, false, false, {encoding: 'buffer'}); -test('.readable() uses strings in objectMode with "binary: false" and object transforms', testObjectMode, foobarObjectChunks, 'readable', null, true, true, false, {stdout: outputObjectGenerator()}); -test('.duplex() uses Buffers with "binary: true"', testObjectMode, simpleChunksBuffer, 'duplex', null, false, false, true); -test('.duplex() uses strings with "binary: true" and .setEncoding("utf8")', testObjectMode, simpleChunks, 'duplex', 'utf8', false, false, true); -test('.duplex() uses strings with "binary: true", .setEncoding("utf8") and "encoding: buffer"', testObjectMode, simpleChunks, 'duplex', 'utf8', false, false, true, {encoding: 'buffer'}); -test('.duplex() uses strings in objectMode with "binary: true" and object transforms', testObjectMode, foobarObjectChunks, 'duplex', null, true, true, true, {stdout: outputObjectGenerator()}); -test('.duplex() uses strings in objectMode with "binary: false"', testObjectMode, simpleLines, 'duplex', null, false, true, false); -test('.duplex() uses strings in objectMode with "binary: false" and .setEncoding("utf8")', testObjectMode, simpleLines, 'duplex', 'utf8', false, true, false); -test('.duplex() uses strings in objectMode with "binary: false", .setEncoding("utf8") and "encoding: buffer"', testObjectMode, simpleChunks, 'duplex', 'utf8', false, false, false, {encoding: 'buffer'}); -test('.duplex() uses strings in objectMode with "binary: false" and object transforms', testObjectMode, foobarObjectChunks, 'duplex', null, true, true, false, {stdout: outputObjectGenerator()}); - -const testObjectSplit = async (t, methodName) => { - const subprocess = getSubprocess(methodName, foobarString, {stdout: getOutputGenerator(simpleFull)(true)}); - const stream = subprocess[methodName]({binary: false}); - await assertChunks(t, stream, [simpleFull], methodName); - await subprocess; -}; - -test('.iterable() "binary: false" does not split lines of strings produced by object transforms', testObjectSplit, 'iterable'); -test('.readable() "binary: false" does not split lines of strings produced by object transforms', testObjectSplit, 'readable'); -test('.duplex() "binary: false" does not split lines of strings produced by object transforms', testObjectSplit, 'duplex'); - -const testMultibyteCharacters = async (t, methodName) => { - const subprocess = getReadWriteSubprocess(); - const stream = subprocess[methodName]({binary: false}); - const assertPromise = assertChunks(t, stream, [`${multibyteChar}${brokenSymbol}`], methodName); - subprocess.stdin.write(multibyteUint8Array.slice(0, breakingLength)); - await once(subprocess.stdout, 'data'); - subprocess.stdin.end(); - await assertPromise; -}; - -test('.iterable() "binary: false" handles partial multibyte characters', testMultibyteCharacters, 'iterable'); -test('.readable() "binary: false" handles partial multibyte characters', testMultibyteCharacters, 'readable'); -test('.duplex() "binary: false" handles partial multibyte characters', testMultibyteCharacters, 'duplex'); diff --git a/test/io/max-buffer.js b/test/io/max-buffer.js deleted file mode 100644 index f29ec515b8..0000000000 --- a/test/io/max-buffer.js +++ /dev/null @@ -1,288 +0,0 @@ -import {Buffer} from 'node:buffer'; -import test from 'ava'; -import getStream from 'get-stream'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {fullStdio} from '../helpers/stdio.js'; -import {getEarlyErrorSubprocess} from '../helpers/early-error.js'; -import {maxBuffer, assertErrorMessage} from '../helpers/max-buffer.js'; -import {foobarArray} from '../helpers/input.js'; - -setFixtureDirectory(); - -const maxBufferMessage = {message: /maxBuffer exceeded/}; -const maxBufferCodeSync = {code: 'ENOBUFS'}; - -const runMaxBuffer = async (t, execaMethod, fdNumber, options) => { - const error = execaMethod === execa - ? await t.throwsAsync(getMaxBufferSubprocess(execaMethod, fdNumber, options), maxBufferMessage) - : t.throws(() => { - getMaxBufferSubprocess(execaMethod, fdNumber, options); - }, maxBufferCodeSync); - t.true(error.isMaxBuffer); - t.is(error.maxBufferInfo, undefined); - return error; -}; - -const getMaxBufferSubprocess = (execaMethod, fdNumber, {length = maxBuffer, ...options} = {}) => - execaMethod('max-buffer.js', [`${fdNumber}`, `${length + 1}`], {...fullStdio, maxBuffer, ...options}); - -const getExpectedOutput = (length = maxBuffer) => '.'.repeat(length); - -const testMaxBufferSuccess = async (t, execaMethod, fdNumber, all) => { - const {isMaxBuffer} = await getMaxBufferSubprocess(execaMethod, fdNumber, {all, length: maxBuffer - 1}); - t.false(isMaxBuffer); -}; - -test('maxBuffer does not affect stdout if too high', testMaxBufferSuccess, execa, 1, false); -test('maxBuffer does not affect stderr if too high', testMaxBufferSuccess, execa, 2, false); -test('maxBuffer does not affect stdio[*] if too high', testMaxBufferSuccess, execa, 3, false); -test('maxBuffer does not affect all if too high', testMaxBufferSuccess, execa, 1, true); -test('maxBuffer does not affect stdout if too high, sync', testMaxBufferSuccess, execaSync, 1, false); -test('maxBuffer does not affect stderr if too high, sync', testMaxBufferSuccess, execaSync, 2, false); -test('maxBuffer does not affect stdio[*] if too high, sync', testMaxBufferSuccess, execaSync, 3, false); -test('maxBuffer does not affect all if too high, sync', testMaxBufferSuccess, execaSync, 1, true); - -const testGracefulExit = async (t, fixtureName, expectedExitCode) => { - const {isMaxBuffer, shortMessage, exitCode, signal, stdout} = await t.throwsAsync( - execa(fixtureName, ['1', '.'.repeat(maxBuffer + 1)], {maxBuffer}), - maxBufferMessage, - ); - t.true(isMaxBuffer); - assertErrorMessage(t, shortMessage); - t.is(exitCode, expectedExitCode); - t.is(signal, undefined); - t.is(stdout, getExpectedOutput()); -}; - -test('maxBuffer terminates stream gracefully, more writes', testGracefulExit, 'noop-repeat.js', 1); -test('maxBuffer terminates stream gracefully, no more writes', testGracefulExit, 'noop-fd.js', 0); - -const testGracefulExitSync = (t, fixtureName) => { - const {isMaxBuffer, shortMessage, exitCode, signal, stdout} = t.throws(() => { - execaSync(fixtureName, ['1', '.'.repeat(maxBuffer + 1)], {maxBuffer, killSignal: 'SIGINT'}); - }, maxBufferCodeSync); - t.true(isMaxBuffer); - assertErrorMessage(t, shortMessage, {execaMethod: execaSync}); - t.is(exitCode, undefined); - t.is(signal, 'SIGINT'); - t.is(stdout, getExpectedOutput()); -}; - -test('maxBuffer terminate stream with killSignal, more writes, sync', testGracefulExitSync, 'noop-repeat.js'); -test('maxBuffer terminate stream with killSignal, no more writes, sync', testGracefulExitSync, 'noop-fd.js'); - -const testMaxBufferLimit = async (t, execaMethod, fdNumber, all) => { - const length = all && execaMethod === execa ? maxBuffer * 2 : maxBuffer; - const {shortMessage, all: allOutput, stdio} = await runMaxBuffer(t, execaMethod, fdNumber, {all, length}); - assertErrorMessage(t, shortMessage, {execaMethod, fdNumber}); - t.is(all ? allOutput : stdio[fdNumber], getExpectedOutput(length)); -}; - -test('maxBuffer truncates stdout', testMaxBufferLimit, execa, 1, false); -test('maxBuffer truncates stderr', testMaxBufferLimit, execa, 2, false); -test('maxBuffer truncates stdio[*]', testMaxBufferLimit, execa, 3, false); -test('maxBuffer truncates all', testMaxBufferLimit, execa, 1, true); -test('maxBuffer truncates stdout, sync', testMaxBufferLimit, execaSync, 1, false); -test('maxBuffer truncates stderr, sync', testMaxBufferLimit, execaSync, 2, false); -test('maxBuffer truncates stdio[*], sync', testMaxBufferLimit, execaSync, 3, false); -test('maxBuffer truncates all, sync', testMaxBufferLimit, execaSync, 1, true); - -const MAX_BUFFER_DEFAULT = 1e8; - -const testMaxBufferDefault = async (t, execaMethod, fdNumber, maxBuffer) => { - const length = MAX_BUFFER_DEFAULT; - const {shortMessage, stdio} = await runMaxBuffer(t, execaMethod, fdNumber, {length: MAX_BUFFER_DEFAULT + 1, maxBuffer}); - assertErrorMessage(t, shortMessage, {execaMethod, fdNumber, length}); - t.is(stdio[fdNumber], getExpectedOutput(length)); -}; - -test('maxBuffer has a default value with stdout', testMaxBufferDefault, execa, 1, undefined); -test('maxBuffer has a default value with stderr', testMaxBufferDefault, execa, 2, undefined); -test('maxBuffer has a default value with stdio[*]', testMaxBufferDefault, execa, 3, undefined); -test('maxBuffer has a default value with stdout, sync', testMaxBufferDefault, execaSync, 1, undefined); -test('maxBuffer has a default value with stderr, sync', testMaxBufferDefault, execaSync, 2, undefined); -test('maxBuffer has a default value with stdio[*], sync', testMaxBufferDefault, execaSync, 3, undefined); -test('maxBuffer has a default value with stdout with fd-specific options', testMaxBufferDefault, execa, 1, {stderr: 1e9}); -test('maxBuffer has a default value with stderr with fd-specific options', testMaxBufferDefault, execa, 2, {stdout: 1e9}); -test('maxBuffer has a default value with stdio[*] with fd-specific options', testMaxBufferDefault, execa, 3, {stdout: 1e9}); -test('maxBuffer has a default value with stdout with empty fd-specific options', testMaxBufferDefault, execa, 1, {}); - -const testFdSpecific = async (t, fdNumber, fdName, execaMethod) => { - const length = 1; - const {shortMessage, stdio} = await runMaxBuffer(t, execaMethod, fdNumber, {maxBuffer: {[fdName]: length}}); - assertErrorMessage(t, shortMessage, {execaMethod, fdNumber, length}); - t.is(stdio[fdNumber], getExpectedOutput(length)); -}; - -test('maxBuffer truncates file descriptors with fd-specific options, stdout', testFdSpecific, 1, 'stdout', execa); -test('maxBuffer truncates file descriptors with fd-specific options, fd1', testFdSpecific, 1, 'fd1', execa); -test('maxBuffer truncates file descriptors with fd-specific options, stderr', testFdSpecific, 2, 'stderr', execa); -test('maxBuffer truncates file descriptors with fd-specific options, fd2', testFdSpecific, 2, 'fd2', execa); -test('maxBuffer truncates file descriptors with fd-specific options, stdout, all', testFdSpecific, 1, 'all', execa); -test('maxBuffer truncates file descriptors with fd-specific options, stderr, all', testFdSpecific, 2, 'all', execa); -test('maxBuffer truncates file descriptors with fd-specific options, fd3', testFdSpecific, 3, 'fd3', execa); -test('maxBuffer.stdout is used for stdout with fd-specific options, stdout, sync', testFdSpecific, 1, 'stdout', execaSync); - -test('maxBuffer does not affect other file descriptors with fd-specific options', async t => { - const {isMaxBuffer} = await getMaxBufferSubprocess(execa, 2, {maxBuffer: {stdout: 1}}); - t.false(isMaxBuffer); -}); - -test('maxBuffer.stdout is used for other file descriptors with fd-specific options, sync', async t => { - const length = 1; - const {shortMessage, stderr} = await runMaxBuffer(t, execaSync, 2, {maxBuffer: {stdout: length}}); - assertErrorMessage(t, shortMessage, {execaMethod: execaSync, fdNumber: 2, length}); - t.is(stderr, getExpectedOutput(length)); -}); - -const testAll = async (t, shouldFail) => { - const difference = shouldFail ? 0 : 1; - const maxBufferStdout = 2; - const maxBufferStderr = 4 - difference; - const {isMaxBuffer, shortMessage, stdout, stderr, all} = await execa( - 'noop-both.js', - ['\n'.repeat(maxBufferStdout - 1), '\n'.repeat(maxBufferStderr - difference)], - { - maxBuffer: {stdout: maxBufferStdout, stderr: maxBufferStderr}, - all: true, - stripFinalNewline: false, - reject: false, - }, - ); - t.is(isMaxBuffer, shouldFail); - if (shouldFail) { - assertErrorMessage(t, shortMessage, {fdNumber: 2, length: maxBufferStderr}); - } - - t.is(stdout, '\n'.repeat(maxBufferStdout)); - t.is(stderr, '\n'.repeat(maxBufferStderr)); - t.is(all, '\n'.repeat(maxBufferStdout + maxBufferStderr)); -}; - -test('maxBuffer.stdout can differ from maxBuffer.stderr, combined with all, below threshold', testAll, false); -test('maxBuffer.stdout can differ from maxBuffer.stderr, combined with all, above threshold', testAll, true); - -const testInvalidFd = async (t, fdName, execaMethod) => { - const {message} = t.throws(() => { - execaMethod('empty.js', {maxBuffer: {[fdName]: 0}}); - }); - t.true(message.includes(`"maxBuffer.${fdName}" is invalid`)); -}; - -test('maxBuffer.stdin is invalid', testInvalidFd, 'stdin', execa); -test('maxBuffer.fd0 is invalid', testInvalidFd, 'fd0', execa); -test('maxBuffer.other is invalid', testInvalidFd, 'other', execa); -test('maxBuffer.fd10 is invalid', testInvalidFd, 'fd10', execa); -test('maxBuffer.stdin is invalid, sync', testInvalidFd, 'stdin', execaSync); -test('maxBuffer.fd0 is invalid, sync', testInvalidFd, 'fd0', execaSync); -test('maxBuffer.other is invalid, sync', testInvalidFd, 'other', execaSync); -test('maxBuffer.fd10 is invalid, sync', testInvalidFd, 'fd10', execaSync); - -const testMaxBufferEncoding = async (t, execaMethod, fdNumber) => { - const {shortMessage, stdio} = await runMaxBuffer(t, execaMethod, fdNumber, {encoding: 'buffer'}); - assertErrorMessage(t, shortMessage, {execaMethod, fdNumber, unit: 'bytes'}); - const stream = stdio[fdNumber]; - t.true(stream instanceof Uint8Array); - t.is(Buffer.from(stream).toString(), getExpectedOutput()); -}; - -test('maxBuffer works with encoding buffer and stdout', testMaxBufferEncoding, execa, 1); -test('maxBuffer works with encoding buffer and stderr', testMaxBufferEncoding, execa, 2); -test('maxBuffer works with encoding buffer and stdio[*]', testMaxBufferEncoding, execa, 3); -test('maxBuffer works with encoding buffer and stdout, sync', testMaxBufferEncoding, execaSync, 1); -test('maxBuffer works with encoding buffer and stderr, sync', testMaxBufferEncoding, execaSync, 2); -test('maxBuffer works with encoding buffer and stdio[*], sync', testMaxBufferEncoding, execaSync, 3); - -const testMaxBufferHex = async (t, fdNumber) => { - const length = maxBuffer / 2; - const {shortMessage, stdio} = await runMaxBuffer(t, execa, fdNumber, {length, encoding: 'hex'}); - assertErrorMessage(t, shortMessage, {fdNumber}); - t.is(stdio[fdNumber], Buffer.from(getExpectedOutput(length)).toString('hex')); -}; - -test('maxBuffer works with other encodings and stdout', testMaxBufferHex, 1); -test('maxBuffer works with other encodings and stderr', testMaxBufferHex, 2); -test('maxBuffer works with other encodings and stdio[*]', testMaxBufferHex, 3); - -const testMaxBufferHexSync = async (t, fdNumber) => { - const length = maxBuffer / 2; - const {isMaxBuffer, stdio} = await getMaxBufferSubprocess(execaSync, fdNumber, {length, encoding: 'hex'}); - t.false(isMaxBuffer); - t.is(stdio[fdNumber], Buffer.from(getExpectedOutput(length + 1)).toString('hex')); -}; - -test('maxBuffer ignores other encodings and stdout, sync', testMaxBufferHexSync, 1); -test('maxBuffer ignores other encodings and stderr, sync', testMaxBufferHexSync, 2); -test('maxBuffer ignores other encodings and stdio[*], sync', testMaxBufferHexSync, 3); - -const testNoMaxBuffer = async (t, fdNumber, buffer) => { - const subprocess = getMaxBufferSubprocess(execa, fdNumber, {buffer}); - const [{isMaxBuffer, stdio}, output] = await Promise.all([ - subprocess, - getStream(subprocess.stdio[fdNumber]), - ]); - t.false(isMaxBuffer); - t.is(stdio[fdNumber], undefined); - t.is(output, getExpectedOutput(maxBuffer + 1)); -}; - -test('do not buffer stdout when `buffer` set to `false`', testNoMaxBuffer, 1, false); -test('do not buffer stdout when `buffer` set to `false`, fd-specific', testNoMaxBuffer, 1, {stdout: false}); -test('do not buffer stderr when `buffer` set to `false`', testNoMaxBuffer, 2, false); -test('do not buffer stderr when `buffer` set to `false`, fd-specific', testNoMaxBuffer, 2, {stderr: false}); -test('do not buffer stdio[*] when `buffer` set to `false`', testNoMaxBuffer, 3, false); -test('do not buffer stdio[*] when `buffer` set to `false`, fd-specific', testNoMaxBuffer, 3, {fd3: false}); - -const testNoMaxBufferSync = (t, fdNumber, buffer) => { - const {isMaxBuffer, stdio} = getMaxBufferSubprocess(execaSync, fdNumber, {buffer}); - t.false(isMaxBuffer); - t.is(stdio[fdNumber], undefined); -}; - -// @todo: add tests for fd3 once the following Node.js bug is fixed. -// https://github.com/nodejs/node/issues/52422 -test('do not buffer stdout when `buffer` set to `false`, sync', testNoMaxBufferSync, 1, false); -test('do not buffer stdout when `buffer` set to `false`, fd-specific, sync', testNoMaxBufferSync, 1, {stdout: false}); -test('do not buffer stderr when `buffer` set to `false`, sync', testNoMaxBufferSync, 2, false); -test('do not buffer stderr when `buffer` set to `false`, fd-specific, sync', testNoMaxBufferSync, 2, {stderr: false}); - -const testMaxBufferAbort = async (t, fdNumber) => { - const subprocess = getMaxBufferSubprocess(execa, fdNumber); - const [{isMaxBuffer, shortMessage}] = await Promise.all([ - t.throwsAsync(subprocess, maxBufferMessage), - t.throwsAsync(getStream(subprocess.stdio[fdNumber]), {code: 'ERR_STREAM_PREMATURE_CLOSE'}), - ]); - t.true(isMaxBuffer); - assertErrorMessage(t, shortMessage, {execaMethod: execa, fdNumber}); -}; - -test('abort stream when hitting maxBuffer with stdout', testMaxBufferAbort, 1); -test('abort stream when hitting maxBuffer with stderr', testMaxBufferAbort, 2); -test('abort stream when hitting maxBuffer with stdio[*]', testMaxBufferAbort, 3); - -test('error.isMaxBuffer is false on early errors', async t => { - const {failed, isMaxBuffer} = await getEarlyErrorSubprocess({reject: false, maxBuffer: 1}); - t.true(failed); - t.false(isMaxBuffer); -}); - -test('maxBuffer works with result.ipcOutput', async t => { - const { - isMaxBuffer, - shortMessage, - message, - stderr, - ipcOutput, - } = await t.throwsAsync(execa('ipc-send-twice.js', {ipc: true, maxBuffer: {ipc: 1}})); - t.true(isMaxBuffer); - t.is(shortMessage, 'Command\'s IPC output was larger than 1 messages: ipc-send-twice.js\nmaxBuffer exceeded'); - t.true(message.endsWith(`\n\n${foobarArray[0]}`)); - t.is(stderr, ''); - t.deepEqual(ipcOutput, [foobarArray[0]]); -}); - -test('maxBuffer is ignored with result.ipcOutput if buffer is false', async t => { - const {ipcOutput} = await execa('ipc-send-twice.js', {ipc: true, maxBuffer: {ipc: 1}, buffer: false}); - t.deepEqual(ipcOutput, []); -}); diff --git a/test/io/output-async.js b/test/io/output-async.js deleted file mode 100644 index 081948418b..0000000000 --- a/test/io/output-async.js +++ /dev/null @@ -1,83 +0,0 @@ -import {once, defaultMaxListeners} from 'node:events'; -import process from 'node:process'; -import {setImmediate} from 'node:timers/promises'; -import test from 'ava'; -import {execa} from '../../index.js'; -import {STANDARD_STREAMS} from '../helpers/stdio.js'; -import {foobarString} from '../helpers/input.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {assertMaxListeners} from '../helpers/listeners.js'; -import {PARALLEL_COUNT} from '../helpers/parallel.js'; - -setFixtureDirectory(); - -const getStandardStreamListeners = stream => Object.fromEntries(stream.eventNames().map(eventName => [eventName, stream.listeners(eventName)])); -const getStandardStreamsListeners = () => STANDARD_STREAMS.map(stream => getStandardStreamListeners(stream)); - -const getComplexStdio = isMultiple => ({ - stdin: ['pipe', 'inherit', ...(isMultiple ? [0, process.stdin] : [])], - stdout: ['pipe', 'inherit', ...(isMultiple ? [1, process.stdout] : [])], - stderr: ['pipe', 'inherit', ...(isMultiple ? [2, process.stderr] : [])], -}); - -const onStdinRemoveListener = () => once(process.stdin, 'removeListener'); - -const testListenersCleanup = async (t, isMultiple) => { - const streamsPreviousListeners = getStandardStreamsListeners(); - const subprocess = execa('empty.js', getComplexStdio(isMultiple)); - t.notDeepEqual(getStandardStreamsListeners(), streamsPreviousListeners); - await Promise.all([subprocess, onStdinRemoveListener()]); - if (isMultiple) { - await onStdinRemoveListener(); - } - - for (const [fdNumber, streamNewListeners] of Object.entries(getStandardStreamsListeners())) { - const defaultListeners = Object.fromEntries(Reflect.ownKeys(streamNewListeners).map(eventName => [eventName, []])); - t.deepEqual(streamNewListeners, {...defaultListeners, ...streamsPreviousListeners[fdNumber]}); - } -}; - -test.serial('process.std* listeners are cleaned up on success with a single input', testListenersCleanup, false); -test.serial('process.std* listeners are cleaned up on success with multiple inputs', testListenersCleanup, true); - -test.serial('Can spawn many subprocesses in parallel', async t => { - const results = await Promise.all( - Array.from({length: PARALLEL_COUNT}, () => execa('noop.js', [foobarString])), - ); - t.true(results.every(({stdout}) => stdout === foobarString)); -}); - -const testMaxListeners = async (t, isMultiple, maxListenersCount) => { - const checkMaxListeners = assertMaxListeners(t); - - for (const standardStream of STANDARD_STREAMS) { - standardStream.setMaxListeners(maxListenersCount); - } - - try { - const results = await Promise.all( - Array.from({length: PARALLEL_COUNT}, () => execa('empty.js', getComplexStdio(isMultiple))), - ); - t.true(results.every(({exitCode}) => exitCode === 0)); - } finally { - await setImmediate(); - await setImmediate(); - checkMaxListeners(); - - for (const standardStream of STANDARD_STREAMS) { - t.is(standardStream.getMaxListeners(), maxListenersCount); - standardStream.setMaxListeners(defaultMaxListeners); - } - } -}; - -test.serial('No warning with maxListeners 1 and ["pipe", "inherit"]', testMaxListeners, false, 1); -test.serial('No warning with maxListeners default and ["pipe", "inherit"]', testMaxListeners, false, defaultMaxListeners); -test.serial('No warning with maxListeners 100 and ["pipe", "inherit"]', testMaxListeners, false, 100); -test.serial('No warning with maxListeners Infinity and ["pipe", "inherit"]', testMaxListeners, false, Number.POSITIVE_INFINITY); -test.serial('No warning with maxListeners 0 and ["pipe", "inherit"]', testMaxListeners, false, 0); -test.serial('No warning with maxListeners 1 and ["pipe", "inherit"], multiple inputs', testMaxListeners, true, 1); -test.serial('No warning with maxListeners default and ["pipe", "inherit"], multiple inputs', testMaxListeners, true, defaultMaxListeners); -test.serial('No warning with maxListeners 100 and ["pipe", "inherit"], multiple inputs', testMaxListeners, true, 100); -test.serial('No warning with maxListeners Infinity and ["pipe", "inherit"], multiple inputs', testMaxListeners, true, Number.POSITIVE_INFINITY); -test.serial('No warning with maxListeners 0 and ["pipe", "inherit"], multiple inputs', testMaxListeners, true, 0); diff --git a/test/io/output-sync.js b/test/io/output-sync.js deleted file mode 100644 index dbe3282a5a..0000000000 --- a/test/io/output-sync.js +++ /dev/null @@ -1,33 +0,0 @@ -import test from 'ava'; -import {execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {throwingGenerator} from '../helpers/generator.js'; -import {foobarString} from '../helpers/input.js'; - -setFixtureDirectory(); - -test('Handles errors with stdout generator, sync', t => { - const cause = new Error(foobarString); - const error = t.throws(() => { - execaSync('noop.js', {stdout: throwingGenerator(cause)()}); - }); - t.is(error.cause, cause); -}); - -test('Handles errors with stdout generator, spawn failure, sync', t => { - const cause = new Error(foobarString); - const error = t.throws(() => { - execaSync('noop.js', {cwd: 'does_not_exist', stdout: throwingGenerator(cause)()}); - }); - t.true(error.failed); - t.is(error.cause.code, 'ENOENT'); -}); - -test('Handles errors with stdout generator, subprocess failure, sync', t => { - const cause = new Error(foobarString); - const error = t.throws(() => { - execaSync('noop-fail.js', ['1'], {stdout: throwingGenerator(cause)()}); - }); - t.true(error.failed); - t.is(error.cause, cause); -}); diff --git a/test/io/pipeline.js b/test/io/pipeline.js deleted file mode 100644 index a1ae8145f0..0000000000 --- a/test/io/pipeline.js +++ /dev/null @@ -1,40 +0,0 @@ -import test from 'ava'; -import {execa} from '../../index.js'; -import {getStdio, STANDARD_STREAMS} from '../helpers/stdio.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {getEarlyErrorSubprocess, expectedEarlyError} from '../helpers/early-error.js'; - -setFixtureDirectory(); - -const testDestroyStandard = async (t, fdNumber) => { - const subprocess = execa('forever.js', {...getStdio(fdNumber, [STANDARD_STREAMS[fdNumber], 'pipe']), timeout: 1}); - await t.throwsAsync(subprocess, {message: /timed out/}); - t.false(STANDARD_STREAMS[fdNumber].destroyed); -}; - -test('Does not destroy process.stdin on subprocess errors', testDestroyStandard, 0); -test('Does not destroy process.stdout on subprocess errors', testDestroyStandard, 1); -test('Does not destroy process.stderr on subprocess errors', testDestroyStandard, 2); - -const testDestroyStandardSpawn = async (t, fdNumber) => { - const error = await t.throwsAsync(getEarlyErrorSubprocess(getStdio(fdNumber, [STANDARD_STREAMS[fdNumber], 'pipe']))); - t.like(error, expectedEarlyError); - t.false(STANDARD_STREAMS[fdNumber].destroyed); -}; - -test('Does not destroy process.stdin on subprocess early errors', testDestroyStandardSpawn, 0); -test('Does not destroy process.stdout on subprocess early errors', testDestroyStandardSpawn, 1); -test('Does not destroy process.stderr on subprocess early errors', testDestroyStandardSpawn, 2); - -const testDestroyStandardStream = async (t, fdNumber) => { - const subprocess = execa('forever.js', getStdio(fdNumber, [STANDARD_STREAMS[fdNumber], 'pipe'])); - const cause = new Error('test'); - subprocess.stdio[fdNumber].destroy(cause); - subprocess.kill(); - t.like(await t.throwsAsync(subprocess), {cause}); - t.false(STANDARD_STREAMS[fdNumber].destroyed); -}; - -test('Does not destroy process.stdin on stream subprocess errors', testDestroyStandardStream, 0); -test('Does not destroy process.stdout on stream subprocess errors', testDestroyStandardStream, 1); -test('Does not destroy process.stderr on stream subprocess errors', testDestroyStandardStream, 2); diff --git a/test/io/strip-newline.js b/test/io/strip-newline.js deleted file mode 100644 index 092ffb9b2d..0000000000 --- a/test/io/strip-newline.js +++ /dev/null @@ -1,56 +0,0 @@ -import test from 'ava'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {fullStdio} from '../helpers/stdio.js'; -import {noopGenerator} from '../helpers/generator.js'; -import {foobarString} from '../helpers/input.js'; - -setFixtureDirectory(); - -// eslint-disable-next-line max-params -const testStripFinalNewline = async (t, fdNumber, stripFinalNewline, shouldStrip, execaMethod) => { - const {stdio} = await execaMethod('noop-fd.js', [`${fdNumber}`, `${foobarString}\n`], {...fullStdio, stripFinalNewline}); - t.is(stdio[fdNumber], `${foobarString}${shouldStrip ? '' : '\n'}`); -}; - -test('stripFinalNewline: default with stdout', testStripFinalNewline, 1, undefined, true, execa); -test('stripFinalNewline: true with stdout', testStripFinalNewline, 1, true, true, execa); -test('stripFinalNewline: false with stdout', testStripFinalNewline, 1, false, false, execa); -test('stripFinalNewline: default with stderr', testStripFinalNewline, 2, undefined, true, execa); -test('stripFinalNewline: true with stderr', testStripFinalNewline, 2, true, true, execa); -test('stripFinalNewline: false with stderr', testStripFinalNewline, 2, false, false, execa); -test('stripFinalNewline: default with stdio[*]', testStripFinalNewline, 3, undefined, true, execa); -test('stripFinalNewline: true with stdio[*]', testStripFinalNewline, 3, true, true, execa); -test('stripFinalNewline: false with stdio[*]', testStripFinalNewline, 3, false, false, execa); -test('stripFinalNewline: default with stdout, fd-specific', testStripFinalNewline, 1, {}, true, execa); -test('stripFinalNewline: true with stdout, fd-specific', testStripFinalNewline, 1, {stdout: true}, true, execa); -test('stripFinalNewline: false with stdout, fd-specific', testStripFinalNewline, 1, {stdout: false}, false, execa); -test('stripFinalNewline: default with stderr, fd-specific', testStripFinalNewline, 2, {}, true, execa); -test('stripFinalNewline: true with stderr, fd-specific', testStripFinalNewline, 2, {stderr: true}, true, execa); -test('stripFinalNewline: false with stderr, fd-specific', testStripFinalNewline, 2, {stderr: false}, false, execa); -test('stripFinalNewline: default with stdio[*], fd-specific', testStripFinalNewline, 3, {}, true, execa); -test('stripFinalNewline: true with stdio[*], fd-specific', testStripFinalNewline, 3, {fd3: true}, true, execa); -test('stripFinalNewline: false with stdio[*], fd-specific', testStripFinalNewline, 3, {fd3: false}, false, execa); -test('stripFinalNewline: default with stdout, sync', testStripFinalNewline, 1, undefined, true, execaSync); -test('stripFinalNewline: true with stdout, sync', testStripFinalNewline, 1, true, true, execaSync); -test('stripFinalNewline: false with stdout, sync', testStripFinalNewline, 1, false, false, execaSync); -test('stripFinalNewline: default with stderr, sync', testStripFinalNewline, 2, undefined, true, execaSync); -test('stripFinalNewline: true with stderr, sync', testStripFinalNewline, 2, true, true, execaSync); -test('stripFinalNewline: false with stderr, sync', testStripFinalNewline, 2, false, false, execaSync); -test('stripFinalNewline: default with stdio[*], sync', testStripFinalNewline, 3, undefined, true, execaSync); -test('stripFinalNewline: true with stdio[*], sync', testStripFinalNewline, 3, true, true, execaSync); -test('stripFinalNewline: false with stdio[*], sync', testStripFinalNewline, 3, false, false, execaSync); -test('stripFinalNewline: default with stdout, fd-specific, sync', testStripFinalNewline, 1, {}, true, execaSync); -test('stripFinalNewline: true with stdout, fd-specific, sync', testStripFinalNewline, 1, {stdout: true}, true, execaSync); -test('stripFinalNewline: false with stdout, fd-specific, sync', testStripFinalNewline, 1, {stdout: false}, false, execaSync); -test('stripFinalNewline: default with stderr, fd-specific, sync', testStripFinalNewline, 2, {}, true, execaSync); -test('stripFinalNewline: true with stderr, fd-specific, sync', testStripFinalNewline, 2, {stderr: true}, true, execaSync); -test('stripFinalNewline: false with stderr, fd-specific, sync', testStripFinalNewline, 2, {stderr: false}, false, execaSync); -test('stripFinalNewline: default with stdio[*], fd-specific, sync', testStripFinalNewline, 3, {}, true, execaSync); -test('stripFinalNewline: true with stdio[*], fd-specific, sync', testStripFinalNewline, 3, {fd3: true}, true, execaSync); -test('stripFinalNewline: false with stdio[*], fd-specific, sync', testStripFinalNewline, 3, {fd3: false}, false, execaSync); - -test('stripFinalNewline is not used in objectMode', async t => { - const {stdout} = await execa('noop-fd.js', ['1', `${foobarString}\n`], {stripFinalNewline: true, stdout: noopGenerator(true, false, true)}); - t.deepEqual(stdout, [`${foobarString}\n`]); -}); diff --git a/test/ipc/buffer-messages.js b/test/ipc/buffer-messages.js deleted file mode 100644 index fb22120573..0000000000 --- a/test/ipc/buffer-messages.js +++ /dev/null @@ -1,75 +0,0 @@ -import test from 'ava'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString, foobarArray} from '../helpers/input.js'; -import {PARALLEL_COUNT} from '../helpers/parallel.js'; - -setFixtureDirectory(); - -const testResultIpc = async (t, options) => { - const {ipcOutput} = await execa('ipc-send-twice.js', {...options, ipc: true}); - t.deepEqual(ipcOutput, foobarArray); -}; - -test('Sets result.ipcOutput', testResultIpc, {}); -test('Sets result.ipcOutput, fd-specific buffer', testResultIpc, {buffer: {stdout: false}}); - -const testResultNoBuffer = async (t, options) => { - const {ipcOutput} = await execa('ipc-send.js', {...options, ipc: true}); - t.deepEqual(ipcOutput, []); -}; - -test('Sets empty result.ipcOutput if buffer is false', testResultNoBuffer, {buffer: false}); -test('Sets empty result.ipcOutput if buffer is false, fd-specific buffer', testResultNoBuffer, {buffer: {ipc: false}}); - -test('Can use IPC methods when buffer is false', async t => { - const subprocess = execa('ipc-send.js', {ipc: true, buffer: false}); - t.is(await subprocess.getOneMessage(), foobarString); - const {ipcOutput} = await subprocess; - t.deepEqual(ipcOutput, []); -}); - -test('Sets empty result.ipcOutput if ipc is false', async t => { - const {ipcOutput} = await execa('empty.js'); - t.deepEqual(ipcOutput, []); -}); - -test('Sets empty result.ipcOutput, sync', t => { - const {ipcOutput} = execaSync('empty.js'); - t.deepEqual(ipcOutput, []); -}); - -const testErrorIpc = async (t, options) => { - const {ipcOutput} = await t.throwsAsync(execa('ipc-send-fail.js', {...options, ipc: true})); - t.deepEqual(ipcOutput, [foobarString]); -}; - -test('Sets error.ipcOutput', testErrorIpc, {}); -test('Sets error.ipcOutput, fd-specific buffer', testErrorIpc, {buffer: {stdout: false}}); - -const testErrorNoBuffer = async (t, options) => { - const {ipcOutput} = await t.throwsAsync(execa('ipc-send-fail.js', {...options, ipc: true})); - t.deepEqual(ipcOutput, []); -}; - -test('Sets empty error.ipcOutput if buffer is false', testErrorNoBuffer, {buffer: false}); -test('Sets empty error.ipcOutput if buffer is false, fd-specific buffer', testErrorNoBuffer, {buffer: {ipc: false}}); - -test('Sets empty error.ipcOutput if ipc is false', async t => { - const {ipcOutput} = await t.throwsAsync(execa('fail.js')); - t.deepEqual(ipcOutput, []); -}); - -test('Sets empty error.ipcOutput, sync', t => { - const {ipcOutput} = t.throws(() => execaSync('fail.js')); - t.deepEqual(ipcOutput, []); -}); - -test.serial('Can retrieve initial IPC messages under heavy load', async t => { - await Promise.all( - Array.from({length: PARALLEL_COUNT}, async (_, index) => { - const {ipcOutput} = await execa('ipc-send-argv.js', [`${index}`], {ipc: true}); - t.deepEqual(ipcOutput, [`${index}`]); - }), - ); -}); diff --git a/test/ipc/forward.js b/test/ipc/forward.js deleted file mode 100644 index 1d5119239c..0000000000 --- a/test/ipc/forward.js +++ /dev/null @@ -1,96 +0,0 @@ -import test from 'ava'; -import {execa} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString, foobarArray} from '../helpers/input.js'; -import {iterateAllMessages, alwaysPass} from '../helpers/ipc.js'; - -setFixtureDirectory(); - -const testParentErrorOne = async (t, filter, buffer) => { - const subprocess = execa('ipc-send.js', {ipc: true, buffer}); - - const promise = subprocess.getOneMessage({filter}); - const cause = new Error(foobarString); - subprocess.emit('error', cause); - t.is(await promise, foobarString); - - const error = await t.throwsAsync(subprocess); - t.is(error.exitCode, undefined); - t.false(error.isTerminated); - t.is(error.cause, cause); - if (buffer) { - t.deepEqual(error.ipcOutput, [foobarString]); - } -}; - -test('"error" event does not interrupt subprocess.getOneMessage(), buffer false', testParentErrorOne, undefined, false); -test('"error" event does not interrupt subprocess.getOneMessage(), buffer true', testParentErrorOne, undefined, true); -test('"error" event does not interrupt subprocess.getOneMessage(), buffer false, filter', testParentErrorOne, alwaysPass, false); -test('"error" event does not interrupt subprocess.getOneMessage(), buffer true, filter', testParentErrorOne, alwaysPass, true); - -const testSubprocessErrorOne = async (t, filter, buffer) => { - const subprocess = execa('ipc-process-error.js', [`${filter}`], {ipc: true, buffer}); - await subprocess.sendMessage(foobarString); - t.is(await subprocess.getOneMessage(), foobarString); - - const {ipcOutput} = await subprocess; - if (buffer) { - t.deepEqual(ipcOutput, [foobarString]); - } -}; - -test('"error" event does not interrupt exports.getOneMessage(), buffer false', testSubprocessErrorOne, false, false); -test('"error" event does not interrupt exports.getOneMessage(), buffer true', testSubprocessErrorOne, false, true); -test('"error" event does not interrupt exports.getOneMessage(), buffer false, filter', testSubprocessErrorOne, true, false); -test('"error" event does not interrupt exports.getOneMessage(), buffer true, filter', testSubprocessErrorOne, true, true); - -const testParentErrorEach = async (t, buffer) => { - const subprocess = execa('ipc-send-twice.js', {ipc: true, buffer}); - - const promise = iterateAllMessages(subprocess); - const cause = new Error(foobarString); - subprocess.emit('error', cause); - - const error = await t.throwsAsync(subprocess); - t.is(error, await t.throwsAsync(promise)); - t.is(error.exitCode, undefined); - t.false(error.isTerminated); - t.is(error.cause, cause); - if (buffer) { - t.deepEqual(error.ipcOutput, foobarArray); - } -}; - -test('"error" event does not interrupt subprocess.getEachMessage(), buffer false', testParentErrorEach, false); -test('"error" event does not interrupt subprocess.getEachMessage(), buffer true', testParentErrorEach, true); - -const testSubprocessErrorEach = async (t, filter, buffer) => { - const subprocess = execa('ipc-iterate-error.js', [`${filter}`], {ipc: true, buffer}); - await subprocess.sendMessage('.'); - t.is(await subprocess.getOneMessage(), '.'); - await subprocess.sendMessage(foobarString); - - const {ipcOutput} = await subprocess; - if (buffer) { - t.deepEqual(ipcOutput, ['.']); - } -}; - -test('"error" event does not interrupt exports.getEachMessage(), buffer false', testSubprocessErrorEach, 'ipc-iterate-error.js', false); -test('"error" event does not interrupt exports.getEachMessage(), buffer true', testSubprocessErrorEach, 'ipc-iterate-error.js', true); - -test('"error" event does not interrupt result.ipcOutput', async t => { - const subprocess = execa('ipc-echo-twice.js', {ipcInput: foobarString}); - - const cause = new Error(foobarString); - subprocess.emit('error', cause); - t.is(await subprocess.getOneMessage(), foobarString); - await subprocess.sendMessage(foobarString); - t.is(await subprocess.getOneMessage(), foobarString); - - const error = await t.throwsAsync(subprocess); - t.is(error.exitCode, undefined); - t.false(error.isTerminated); - t.is(error.cause, cause); - t.deepEqual(error.ipcOutput, [foobarString, foobarString]); -}); diff --git a/test/ipc/get-each.js b/test/ipc/get-each.js deleted file mode 100644 index 773ef6ee41..0000000000 --- a/test/ipc/get-each.js +++ /dev/null @@ -1,213 +0,0 @@ -import {scheduler} from 'node:timers/promises'; -import test from 'ava'; -import {execa} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString, foobarArray} from '../helpers/input.js'; -import {PARALLEL_COUNT} from '../helpers/parallel.js'; -import {iterateAllMessages} from '../helpers/ipc.js'; - -setFixtureDirectory(); - -test('Can iterate over IPC messages', async t => { - let count = 0; - const subprocess = execa('ipc-send-twice.js', {ipc: true}); - for await (const message of subprocess.getEachMessage()) { - t.is(message, foobarArray[count++]); - } - - const {ipcOutput} = await subprocess; - t.deepEqual(ipcOutput, foobarArray); -}); - -test('Can iterate over IPC messages in subprocess', async t => { - const subprocess = execa('ipc-iterate.js', {ipc: true}); - - await subprocess.sendMessage('.'); - await subprocess.sendMessage('.'); - await subprocess.sendMessage(foobarString); - - const {ipcOutput} = await subprocess; - t.deepEqual(ipcOutput, ['.', '.']); -}); - -test('subprocess.getEachMessage() can be called twice at the same time', async t => { - const subprocess = execa('ipc-send-twice.js', {ipc: true}); - t.deepEqual( - await Promise.all([iterateAllMessages(subprocess), iterateAllMessages(subprocess)]), - [foobarArray, foobarArray], - ); - - const {ipcOutput} = await subprocess; - t.deepEqual(ipcOutput, foobarArray); -}); - -const iterateAndBreak = async (t, subprocess) => { - // eslint-disable-next-line no-unreachable-loop - for await (const message of subprocess.getEachMessage()) { - t.is(message, foobarString); - break; - } -}; - -test('Breaking in subprocess.getEachMessage() disconnects', async t => { - const subprocess = execa('ipc-iterate-send.js', {ipc: true}); - await iterateAndBreak(t, subprocess); - const {ipcOutput} = await subprocess; - t.deepEqual(ipcOutput, [foobarString]); -}); - -test('Breaking from subprocess.getEachMessage() awaits the subprocess', async t => { - const subprocess = execa('ipc-send-wait-print.js', {ipc: true}); - await iterateAndBreak(t, subprocess); - - const {ipcOutput, stdout} = await subprocess; - t.deepEqual(ipcOutput, [foobarString]); - t.is(stdout, '.'); -}); - -test('Breaking from exports.getEachMessage() disconnects', async t => { - const subprocess = execa('ipc-iterate-break.js', {ipc: true}); - - t.is(await subprocess.getOneMessage(), foobarString); - await subprocess.sendMessage(foobarString); - const ipcError = await t.throwsAsync(subprocess.getOneMessage()); - t.true(ipcError.message.includes('subprocess.getOneMessage() could not complete')); - - const {ipcOutput} = await subprocess; - t.deepEqual(ipcOutput, [foobarString]); -}); - -const iterateAndThrow = async (t, subprocess, cause) => { - // eslint-disable-next-line no-unreachable-loop - for await (const message of subprocess.getEachMessage()) { - t.is(message, foobarString); - throw cause; - } -}; - -test('Throwing from subprocess.getEachMessage() disconnects', async t => { - const subprocess = execa('ipc-iterate-send.js', {ipc: true}); - - const cause = new Error(foobarString); - t.is(await t.throwsAsync(iterateAndThrow(t, subprocess, cause)), cause); - - const {ipcOutput} = await subprocess; - t.deepEqual(ipcOutput, [foobarString]); -}); - -test('Throwing from subprocess.getEachMessage() awaits the subprocess', async t => { - const subprocess = execa('ipc-send-wait-print.js', {ipc: true}); - const cause = new Error(foobarString); - t.is(await t.throwsAsync(iterateAndThrow(t, subprocess, cause)), cause); - - const {ipcOutput, stdout} = await subprocess; - t.deepEqual(ipcOutput, [foobarString]); - t.is(stdout, '.'); -}); - -test('Throwing from exports.getEachMessage() disconnects', async t => { - const subprocess = execa('ipc-iterate-throw.js', {ipc: true}); - - t.is(await subprocess.getOneMessage(), foobarString); - await subprocess.sendMessage(foobarString); - const ipcError = await t.throwsAsync(subprocess.getOneMessage()); - t.true(ipcError.message.includes('subprocess.getOneMessage() could not complete')); - - const {exitCode, isTerminated, message, ipcOutput} = await t.throwsAsync(subprocess); - t.is(exitCode, 1); - t.false(isTerminated); - t.true(message.includes(`Error: ${foobarString}`)); - t.deepEqual(ipcOutput, [foobarString]); -}); - -test.serial('Can send many messages at once with exports.getEachMessage()', async t => { - const subprocess = execa('ipc-iterate.js', {ipc: true}); - await Promise.all(Array.from({length: PARALLEL_COUNT}, (_, index) => subprocess.sendMessage(index))); - await subprocess.sendMessage(foobarString); - - const {ipcOutput} = await subprocess; - t.deepEqual(ipcOutput, Array.from({length: PARALLEL_COUNT}, (_, index) => index)); -}); - -test('subprocess.getOneMessage() can be called multiple times in a row, buffer true', async t => { - const subprocess = execa('ipc-print-many-each.js', [`${PARALLEL_COUNT}`], {ipc: true}); - const indexes = Array.from({length: PARALLEL_COUNT}, (_, index) => `${index}`); - await Promise.all(indexes.map(index => subprocess.sendMessage(index))); - - const {stdout} = await subprocess; - const expectedOutput = indexes.join('\n'); - t.is(stdout, expectedOutput); -}); - -test('Disconnecting in the current process stops exports.getEachMessage()', async t => { - const subprocess = execa('ipc-iterate-print.js', {ipc: true}); - t.is(await subprocess.getOneMessage(), foobarString); - await subprocess.sendMessage('.'); - subprocess.disconnect(); - - const {stdout} = await subprocess; - t.is(stdout, '.'); -}); - -test('Disconnecting in the subprocess stops subprocess.getEachMessage()', async t => { - const subprocess = execa('ipc-send-disconnect.js', {ipc: true}); - for await (const message of subprocess.getEachMessage()) { - t.is(message, foobarString); - } - - const {ipcOutput} = await subprocess; - t.deepEqual(ipcOutput, [foobarString]); -}); - -test('Exiting the subprocess stops subprocess.getEachMessage()', async t => { - const subprocess = execa('ipc-send.js', {ipc: true}); - for await (const message of subprocess.getEachMessage()) { - t.is(message, foobarString); - } - - const {ipcOutput} = await subprocess; - t.deepEqual(ipcOutput, [foobarString]); -}); - -const testCleanupListeners = async (t, buffer) => { - const subprocess = execa('ipc-send.js', {ipc: true, buffer}); - - t.is(subprocess.listenerCount('message'), 1); - t.is(subprocess.listenerCount('disconnect'), 1); - - const promise = iterateAllMessages(subprocess); - t.is(subprocess.listenerCount('message'), 1); - t.is(subprocess.listenerCount('disconnect'), 1); - t.deepEqual(await promise, [foobarString]); - - t.is(subprocess.listenerCount('message'), 0); - t.is(subprocess.listenerCount('disconnect'), 0); -}; - -test('Cleans up subprocess.getEachMessage() listeners, buffer false', testCleanupListeners, false); -test('Cleans up subprocess.getEachMessage() listeners, buffer true', testCleanupListeners, true); - -const sendContinuousMessages = async subprocess => { - while (subprocess.connected) { - for (let index = 0; index < 10; index += 1) { - subprocess.emit('message', foobarString); - } - - // eslint-disable-next-line no-await-in-loop - await scheduler.yield(); - } -}; - -test.serial('Handles buffered messages when disconnecting', async t => { - const subprocess = execa('ipc-send-fail.js', {ipc: true, buffer: false}); - - const promise = subprocess.getOneMessage(); - subprocess.emit('message', foobarString); - t.is(await promise, foobarString); - sendContinuousMessages(subprocess); - - const {exitCode, isTerminated, ipcOutput} = await t.throwsAsync(iterateAllMessages(subprocess)); - t.is(exitCode, 1); - t.false(isTerminated); - t.deepEqual(ipcOutput, []); -}); diff --git a/test/ipc/get-one.js b/test/ipc/get-one.js deleted file mode 100644 index 4552151e31..0000000000 --- a/test/ipc/get-one.js +++ /dev/null @@ -1,133 +0,0 @@ -import test from 'ava'; -import {execa} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString, foobarArray} from '../helpers/input.js'; -import {alwaysPass} from '../helpers/ipc.js'; -import {PARALLEL_COUNT} from '../helpers/parallel.js'; - -setFixtureDirectory(); - -test('subprocess.getOneMessage() can filter messages', async t => { - const subprocess = execa('ipc-send-twice.js', {ipc: true}); - const message = await subprocess.getOneMessage({filter: message => message === foobarArray[1]}); - t.is(message, foobarArray[1]); - - const {ipcOutput} = await subprocess; - t.deepEqual(ipcOutput, foobarArray); -}); - -test('exports.getOneMessage() can filter messages', async t => { - const subprocess = execa('ipc-echo-filter.js', {ipc: true}); - await subprocess.sendMessage(foobarArray[0]); - await subprocess.sendMessage(foobarArray[1]); - - const {ipcOutput} = await subprocess; - t.deepEqual(ipcOutput, [foobarArray[1]]); -}); - -test('Throwing from subprocess.getOneMessage() filter disconnects', async t => { - const subprocess = execa('ipc-send-get.js', {ipc: true}); - const error = new Error(foobarString); - t.is(await t.throwsAsync(subprocess.getOneMessage({ - filter() { - throw error; - }, - })), error); - - const {exitCode, isTerminated, message, ipcOutput} = await t.throwsAsync(subprocess); - t.is(exitCode, 1); - t.false(isTerminated); - t.true(message.includes('Error: getOneMessage() could not complete')); - t.deepEqual(ipcOutput, [foobarString]); -}); - -test('Throwing from exports.getOneMessage() filter disconnects', async t => { - const subprocess = execa('ipc-get-filter-throw.js', {ipcInput: 0}); - await t.throwsAsync(subprocess.getOneMessage(), { - message: /subprocess.getOneMessage\(\) could not complete/, - }); - - const {exitCode, isTerminated, message, ipcOutput} = await t.throwsAsync(subprocess); - t.is(exitCode, 1); - t.false(isTerminated); - t.true(message.includes(`Error: ${foobarString}`)); - t.deepEqual(ipcOutput, []); -}); - -test.serial('Can retrieve initial IPC messages under heavy load', async t => { - await Promise.all( - Array.from({length: PARALLEL_COUNT}, async (_, index) => { - const subprocess = execa('ipc-send-argv.js', [`${index}`], {ipc: true, buffer: false}); - t.is(await subprocess.getOneMessage(), `${index}`); - await subprocess; - }), - ); -}); - -const testTwice = async (t, buffer, filter) => { - const subprocess = execa('ipc-send.js', {ipc: true, buffer}); - t.deepEqual( - await Promise.all([subprocess.getOneMessage({filter}), subprocess.getOneMessage({filter})]), - [foobarString, foobarString], - ); - await subprocess; -}; - -test('subprocess.getOneMessage() can be called twice at the same time, buffer false', testTwice, false, undefined); -test('subprocess.getOneMessage() can be called twice at the same time, buffer true', testTwice, true, undefined); -test('subprocess.getOneMessage() can be called twice at the same time, buffer false, filter', testTwice, false, alwaysPass); -test('subprocess.getOneMessage() can be called twice at the same time, buffer true, filter', testTwice, true, alwaysPass); - -const testCleanupListeners = async (t, buffer, filter) => { - const subprocess = execa('ipc-send.js', {ipc: true, buffer}); - - t.is(subprocess.listenerCount('message'), 1); - t.is(subprocess.listenerCount('disconnect'), 1); - - const promise = subprocess.getOneMessage({filter}); - t.is(subprocess.listenerCount('message'), 1); - t.is(subprocess.listenerCount('disconnect'), 1); - - t.is(await promise, foobarString); - await subprocess; - - t.is(subprocess.listenerCount('message'), 0); - t.is(subprocess.listenerCount('disconnect'), 0); -}; - -test('Cleans up subprocess.getOneMessage() listeners, buffer false', testCleanupListeners, false, undefined); -test('Cleans up subprocess.getOneMessage() listeners, buffer true', testCleanupListeners, true, undefined); -test('Cleans up subprocess.getOneMessage() listeners, buffer false, filter', testCleanupListeners, false, alwaysPass); -test('Cleans up subprocess.getOneMessage() listeners, buffer true, filter', testCleanupListeners, true, alwaysPass); - -const testParentDisconnect = async (t, buffer, filter) => { - const subprocess = execa('ipc-get-send-get.js', [`${filter}`], {ipc: true, buffer}); - await subprocess.sendMessage(foobarString); - t.is(await subprocess.getOneMessage(), foobarString); - - subprocess.disconnect(); - - const {exitCode, isTerminated, message} = await t.throwsAsync(subprocess); - t.is(exitCode, 1); - t.false(isTerminated); - if (buffer) { - t.true(message.includes('Error: getOneMessage() could not complete')); - } -}; - -test('subprocess.disconnect() interrupts exports.getOneMessage(), buffer false', testParentDisconnect, false, false); -test('subprocess.disconnect() interrupts exports.getOneMessage(), buffer true', testParentDisconnect, true, false); -test('subprocess.disconnect() interrupts exports.getOneMessage(), buffer false, filter', testParentDisconnect, false, true); -test('subprocess.disconnect() interrupts exports.getOneMessage(), buffer true, filter', testParentDisconnect, true, false); - -const testSubprocessDisconnect = async (t, buffer, filter) => { - const subprocess = execa('empty.js', {ipc: true, buffer}); - const {message} = await t.throwsAsync(subprocess.getOneMessage({filter})); - t.true(message.includes('subprocess.getOneMessage() could not complete')); - await subprocess; -}; - -test('Subprocess exit interrupts subprocess.getOneMessage(), buffer false', testSubprocessDisconnect, false, undefined); -test('Subprocess exit interrupts subprocess.getOneMessage(), buffer true', testSubprocessDisconnect, true, undefined); -test('Subprocess exit interrupts subprocess.getOneMessage(), buffer false, filter', testSubprocessDisconnect, false, alwaysPass); -test('Subprocess exit interrupts subprocess.getOneMessage(), buffer true, filter', testSubprocessDisconnect, true, alwaysPass); diff --git a/test/ipc/graceful.js b/test/ipc/graceful.js deleted file mode 100644 index e4d19ff738..0000000000 --- a/test/ipc/graceful.js +++ /dev/null @@ -1,216 +0,0 @@ -import {getEventListeners} from 'node:events'; -import {setTimeout} from 'node:timers/promises'; -import test from 'ava'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString} from '../helpers/input.js'; - -setFixtureDirectory(); - -test('Graceful cancelSignal can be already aborted', async t => { - const {isCanceled, isGracefullyCanceled, isTerminated, exitCode, ipcOutput} = await t.throwsAsync(execa('graceful-send.js', {cancelSignal: AbortSignal.abort(foobarString), gracefulCancel: true, forceKillAfterDelay: false})); - t.true(isCanceled); - t.true(isGracefullyCanceled); - t.false(isTerminated); - t.is(exitCode, 0); - t.deepEqual(ipcOutput, [foobarString]); -}); - -test('Graceful cancelSignal can be aborted', async t => { - const controller = new AbortController(); - const subprocess = execa('graceful-send-twice.js', {cancelSignal: controller.signal, gracefulCancel: true, forceKillAfterDelay: false}); - t.false(await subprocess.getOneMessage()); - controller.abort(foobarString); - const {isCanceled, isGracefullyCanceled, isTerminated, exitCode, ipcOutput} = await t.throwsAsync(subprocess); - t.true(isCanceled); - t.true(isGracefullyCanceled); - t.false(isTerminated); - t.is(exitCode, 0); - t.deepEqual(ipcOutput, [false, foobarString]); -}); - -test('Graceful cancelSignal can be never aborted', async t => { - const controller = new AbortController(); - const subprocess = execa('graceful-send-fast.js', {cancelSignal: controller.signal, gracefulCancel: true}); - t.false(await subprocess.getOneMessage()); - await subprocess; -}); - -test('Graceful cancelSignal can be already aborted but not used', async t => { - const subprocess = execa('ipc-send-get.js', {cancelSignal: AbortSignal.abort(foobarString), gracefulCancel: true, forceKillAfterDelay: false}); - t.is(await subprocess.getOneMessage(), foobarString); - await setTimeout(1e3); - await subprocess.sendMessage('.'); - const {isCanceled, isGracefullyCanceled, isTerminated, exitCode, ipcOutput} = await t.throwsAsync(subprocess); - t.true(isCanceled); - t.true(isGracefullyCanceled); - t.false(isTerminated); - t.is(exitCode, 0); - t.deepEqual(ipcOutput, [foobarString]); -}); - -test('Graceful cancelSignal can be aborted but not used', async t => { - const controller = new AbortController(); - const subprocess = execa('ipc-send-get.js', {cancelSignal: controller.signal, gracefulCancel: true, forceKillAfterDelay: false}); - t.is(await subprocess.getOneMessage(), foobarString); - controller.abort(foobarString); - await setTimeout(1e3); - await subprocess.sendMessage(foobarString); - const {isCanceled, isGracefullyCanceled, isTerminated, exitCode, ipcOutput} = await t.throwsAsync(subprocess); - t.true(isCanceled); - t.true(isGracefullyCanceled); - t.false(isTerminated); - t.is(exitCode, 0); - t.deepEqual(ipcOutput, [foobarString]); -}); - -test('Graceful cancelSignal can be never aborted nor used', async t => { - const controller = new AbortController(); - const subprocess = execa('empty.js', {cancelSignal: controller.signal, gracefulCancel: true}); - t.is(getEventListeners(controller.signal, 'abort').length, 1); - await subprocess; - t.is(getEventListeners(controller.signal, 'abort').length, 0); -}); - -test('Graceful cancelSignal can be aborted twice', async t => { - const controller = new AbortController(); - const subprocess = execa('graceful-send-twice.js', {cancelSignal: controller.signal, gracefulCancel: true, forceKillAfterDelay: false}); - t.false(await subprocess.getOneMessage()); - controller.abort(foobarString); - controller.abort('.'); - const {isCanceled, isGracefullyCanceled, isTerminated, exitCode, ipcOutput} = await t.throwsAsync(subprocess); - t.true(isCanceled); - t.true(isGracefullyCanceled); - t.false(isTerminated); - t.is(exitCode, 0); - t.deepEqual(ipcOutput, [false, foobarString]); -}); - -test('Graceful cancelSignal cannot be manually aborted after disconnection', async t => { - const controller = new AbortController(); - const subprocess = execa('empty.js', {cancelSignal: controller.signal, gracefulCancel: true}); - subprocess.disconnect(); - controller.abort(foobarString); - const {isCanceled, isGracefullyCanceled, isTerminated, exitCode, ipcOutput, originalMessage} = await t.throwsAsync(subprocess); - t.false(isCanceled); - t.false(isGracefullyCanceled); - t.false(isTerminated); - t.is(exitCode, 0); - t.deepEqual(ipcOutput, []); - t.is(originalMessage, '`cancelSignal`\'s `controller.abort()` cannot be used: the subprocess has already exited or disconnected.'); -}); - -test('Graceful cancelSignal can disconnect after being manually aborted', async t => { - const controller = new AbortController(); - const subprocess = execa('graceful-disconnect.js', {cancelSignal: controller.signal, gracefulCancel: true}); - controller.abort(foobarString); - t.is(await subprocess.getOneMessage(), foobarString); - subprocess.disconnect(); - const {isCanceled, isGracefullyCanceled, isTerminated, exitCode, ipcOutput} = await t.throwsAsync(subprocess); - t.true(isCanceled); - t.true(isGracefullyCanceled); - t.false(isTerminated); - t.is(exitCode, 0); - t.deepEqual(ipcOutput, [foobarString]); -}); - -test('Graceful cancelSignal is automatically aborted on disconnection', async t => { - const controller = new AbortController(); - const subprocess = execa('graceful-send-print.js', {cancelSignal: controller.signal, gracefulCancel: true}); - t.false(await subprocess.getOneMessage()); - subprocess.disconnect(); - const {isCanceled, isGracefullyCanceled, ipcOutput, stdout} = await subprocess; - t.false(isCanceled); - t.false(isGracefullyCanceled); - t.deepEqual(ipcOutput, [false]); - t.true(stdout.includes('Error: `cancelSignal` aborted: the parent process disconnected.')); -}); - -test('getCancelSignal() aborts if already disconnected', async t => { - const controller = new AbortController(); - const subprocess = execa('graceful-print.js', {cancelSignal: controller.signal, gracefulCancel: true}); - subprocess.disconnect(); - const {isCanceled, isGracefullyCanceled, ipcOutput, stdout} = await subprocess; - t.false(isCanceled); - t.false(isGracefullyCanceled); - t.deepEqual(ipcOutput, []); - t.true(stdout.includes('Error: `cancelSignal` aborted: the parent process disconnected.')); -}); - -test('getCancelSignal() fails if no IPC', async t => { - const {isCanceled, isGracefullyCanceled, isTerminated, exitCode, ipcOutput, stderr} = await t.throwsAsync(execa('graceful-none.js')); - t.false(isCanceled); - t.false(isGracefullyCanceled); - t.false(isTerminated); - t.is(exitCode, 1); - t.deepEqual(ipcOutput, []); - t.true(stderr.includes('Error: `getCancelSignal()` cannot be used without setting the `cancelSignal` subprocess option.')); -}); - -test.serial('getCancelSignal() hangs if cancelSignal without gracefulCancel', async t => { - const controller = new AbortController(); - const {timedOut, isCanceled, isGracefullyCanceled, signal, ipcOutput} = await t.throwsAsync(execa('graceful-wait.js', {ipc: true, cancelSignal: controller.signal, timeout: 1e3})); - t.true(timedOut); - t.false(isCanceled); - t.false(isGracefullyCanceled); - t.is(signal, 'SIGTERM'); - t.deepEqual(ipcOutput, []); -}); - -test('Subprocess cancelSignal does not keep subprocess alive', async t => { - const controller = new AbortController(); - const {ipcOutput} = await execa('graceful-ref.js', {cancelSignal: controller.signal, gracefulCancel: true}); - t.deepEqual(ipcOutput, []); -}); - -test('Subprocess can send a message right away', async t => { - const controller = new AbortController(); - const {ipcOutput} = await execa('graceful-send-string.js', {cancelSignal: controller.signal, gracefulCancel: true}); - t.deepEqual(ipcOutput, [foobarString]); -}); - -test('Subprocess can receive a message right away', async t => { - const controller = new AbortController(); - const {ipcOutput} = await execa('graceful-echo.js', {cancelSignal: controller.signal, gracefulCancel: true, ipcInput: foobarString}); - t.deepEqual(ipcOutput, [foobarString]); -}); - -test('getCancelSignal() can be called twice', async t => { - const {isCanceled, isGracefullyCanceled, isTerminated, exitCode, ipcOutput} = await t.throwsAsync(execa('graceful-twice.js', {cancelSignal: AbortSignal.abort(foobarString), gracefulCancel: true, forceKillAfterDelay: false})); - t.true(isCanceled); - t.true(isGracefullyCanceled); - t.false(isTerminated); - t.is(exitCode, 0); - t.deepEqual(ipcOutput, [foobarString]); -}); - -test('Graceful cancelSignal can use cancelSignal.onabort', async t => { - const controller = new AbortController(); - const subprocess = execa('graceful-listener.js', {cancelSignal: controller.signal, gracefulCancel: true, forceKillAfterDelay: false}); - t.is(await subprocess.getOneMessage(), '.'); - controller.abort(foobarString); - const {isCanceled, isGracefullyCanceled, isTerminated, exitCode, ipcOutput} = await t.throwsAsync(subprocess); - t.true(isCanceled); - t.true(isGracefullyCanceled); - t.false(isTerminated); - t.is(exitCode, 0); - t.deepEqual(ipcOutput, ['.', foobarString]); -}); - -test('Graceful cancelSignal abort reason cannot be directly received', async t => { - const subprocess = execa('graceful-send-echo.js', {cancelSignal: AbortSignal.abort(foobarString), gracefulCancel: true, forceKillAfterDelay: false}); - await setTimeout(0); - await subprocess.sendMessage('.'); - const {isCanceled, isGracefullyCanceled, isTerminated, exitCode, ipcOutput} = await t.throwsAsync(subprocess); - t.true(isCanceled); - t.true(isGracefullyCanceled); - t.false(isTerminated); - t.is(exitCode, 0); - t.deepEqual(ipcOutput, ['.', foobarString]); -}); - -test('error.isGracefullyCanceled is always false with execaSync()', t => { - const {isCanceled, isGracefullyCanceled} = execaSync('empty.js'); - t.false(isCanceled); - t.false(isGracefullyCanceled); -}); diff --git a/test/ipc/incoming.js b/test/ipc/incoming.js deleted file mode 100644 index a36e658e28..0000000000 --- a/test/ipc/incoming.js +++ /dev/null @@ -1,56 +0,0 @@ -import test from 'ava'; -import {execa} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString} from '../helpers/input.js'; -import {alwaysPass} from '../helpers/ipc.js'; -import {PARALLEL_COUNT} from '../helpers/parallel.js'; - -setFixtureDirectory(); - -const testSeriesParent = async (t, buffer, filter) => { - const subprocess = execa('ipc-send-many.js', [`${PARALLEL_COUNT}`], {ipc: true, buffer}); - - for (let index = 0; index < PARALLEL_COUNT; index += 1) { - // eslint-disable-next-line no-await-in-loop - t.is(await subprocess.getOneMessage({filter}), index); - } - - const {ipcOutput} = await subprocess; - if (buffer) { - t.deepEqual(ipcOutput, Array.from({length: PARALLEL_COUNT}, (_, index) => index)); - } -}; - -test('subprocess.getOneMessage() can be called multiple times in a row, buffer false', testSeriesParent, false, undefined); -test('subprocess.getOneMessage() can be called multiple times in a row, buffer true', testSeriesParent, true, undefined); -test('subprocess.getOneMessage() can be called multiple times in a row, buffer false, filter', testSeriesParent, false, alwaysPass); -test('subprocess.getOneMessage() can be called multiple times in a row, buffer true, filter', testSeriesParent, true, alwaysPass); - -const testSeriesSubprocess = async (t, filter) => { - const subprocess = execa('ipc-print-many.js', [`${PARALLEL_COUNT}`, `${filter}`], {ipc: true}); - const indexes = Array.from({length: PARALLEL_COUNT}, (_, index) => `${index}`); - await Promise.all(indexes.map(index => subprocess.sendMessage(index))); - - const {stdout} = await subprocess; - const expectedOutput = indexes.join('\n'); - t.is(stdout, expectedOutput); -}; - -test('exports.getOneMessage() can be called multiple times in a row', testSeriesSubprocess, false); -test('exports.getOneMessage() can be called multiple times in a row, filter', testSeriesSubprocess, true); - -test('Can iterate multiple times over IPC messages in subprocess', async t => { - const subprocess = execa('ipc-iterate-twice.js', {ipc: true}); - - t.is(await subprocess.getOneMessage(), foobarString); - await subprocess.sendMessage('.'); - t.is(await subprocess.getOneMessage(), '0.'); - await subprocess.sendMessage(foobarString); - t.is(await subprocess.getOneMessage(), foobarString); - await subprocess.sendMessage('.'); - t.is(await subprocess.getOneMessage(), '1.'); - await subprocess.sendMessage(foobarString); - - const {ipcOutput} = await subprocess; - t.deepEqual(ipcOutput, [foobarString, '0.', foobarString, '1.']); -}); diff --git a/test/ipc/ipc-input.js b/test/ipc/ipc-input.js deleted file mode 100644 index 57c5e67609..0000000000 --- a/test/ipc/ipc-input.js +++ /dev/null @@ -1,53 +0,0 @@ -import test from 'ava'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString} from '../helpers/input.js'; - -setFixtureDirectory(); - -const testSuccess = async (t, options) => { - const {ipcOutput} = await execa('ipc-echo.js', {ipcInput: foobarString, ...options}); - t.deepEqual(ipcOutput, [foobarString]); -}; - -test('Sends a message with the "ipcInput" option, ipc undefined', testSuccess, {}); -test('Sends a message with the "ipcInput" option, ipc true', testSuccess, {ipc: true}); - -test('Cannot use the "ipcInput" option with "ipc" false', t => { - t.throws(() => { - execa('empty.js', {ipcInput: foobarString, ipc: false}); - }, {message: /unless the `ipc` option is `true`/}); -}); - -test('Cannot use the "ipcInput" option with execaSync()', t => { - t.throws(() => { - execaSync('empty.js', {ipcInput: foobarString}); - }, {message: /The "ipcInput" option cannot be used with synchronous/}); -}); - -test('Invalid "ipcInput" option v8 format', t => { - const {message, cause} = t.throws(() => { - execa('empty.js', {ipcInput() {}}); - }); - t.is(message, 'The `ipcInput` option is not serializable with a structured clone.'); - t.is(cause.message, 'ipcInput() {} could not be cloned.'); -}); - -test('Invalid "ipcInput" option JSON format', t => { - const {message, cause} = t.throws(() => { - execa('empty.js', {ipcInput: 0n, serialization: 'json'}); - }); - t.is(message, 'The `ipcInput` option is not serializable with JSON.'); - t.is(cause.message, 'Do not know how to serialize a BigInt'); -}); - -test('Handles "ipcInput" option during sending', async t => { - const {message, cause} = await t.throwsAsync(execa('empty.js', {ipcInput: 0n})); - t.true(message.includes('subprocess.sendMessage()\'s argument type is invalid: the message cannot be serialized: 0.')); - t.true(cause.cause.message.includes('The "message" argument must be one of type string')); -}); - -test.serial('Can use "ipcInput" option even if the subprocess is not listening to messages', async t => { - const {ipcOutput} = await execa('empty.js', {ipcInput: foobarString}); - t.deepEqual(ipcOutput, []); -}); diff --git a/test/ipc/outgoing.js b/test/ipc/outgoing.js deleted file mode 100644 index d4c0fae33f..0000000000 --- a/test/ipc/outgoing.js +++ /dev/null @@ -1,88 +0,0 @@ -import test from 'ava'; -import {execa} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString} from '../helpers/input.js'; -import {alwaysPass, subprocessGetOne, subprocessGetFirst} from '../helpers/ipc.js'; -import {PARALLEL_COUNT} from '../helpers/parallel.js'; - -setFixtureDirectory(); - -const testSendHoldParent = async (t, getMessage, buffer, filter) => { - const subprocess = execa('ipc-iterate.js', {ipc: true, buffer}); - - await subprocess.sendMessage(0); - t.is(await subprocess.getOneMessage({filter}), 0); - - const messages = Array.from({length: PARALLEL_COUNT}, (_, index) => index + 1); - await Promise.all([ - ...messages.map(message => subprocess.sendMessage(message)), - subprocess.sendMessage(foobarString), - subprocess.emit('message', '.'), - ]); - t.is(await getMessage(subprocess, {filter}), '.'); - - const {ipcOutput} = await subprocess; - if (buffer) { - const expectedOutput = [0, '.', ...messages]; - t.deepEqual(ipcOutput, expectedOutput); - } -}; - -test('Multiple parallel subprocess.sendMessage() + subprocess.getOneMessage(), buffer false', testSendHoldParent, subprocessGetOne, false, undefined); -test('Multiple parallel subprocess.sendMessage() + subprocess.getOneMessage(), buffer true', testSendHoldParent, subprocessGetOne, true, undefined); -test('Multiple parallel subprocess.sendMessage() + subprocess.getOneMessage(), buffer false, filter', testSendHoldParent, subprocessGetOne, false, alwaysPass); -test('Multiple parallel subprocess.sendMessage() + subprocess.getOneMessage(), buffer true, filter', testSendHoldParent, subprocessGetOne, true, alwaysPass); -test('Multiple parallel subprocess.sendMessage() + subprocess.getEachMessage(), buffer false', testSendHoldParent, subprocessGetFirst, false, undefined); -test('Multiple parallel subprocess.sendMessage() + subprocess.getEachMessage(), buffer true', testSendHoldParent, subprocessGetFirst, true, undefined); - -const testSendHoldSubprocess = async (t, filter, isGetEach) => { - const {ipcOutput} = await execa('ipc-iterate-back.js', [`${filter}`, `${isGetEach}`], {ipcInput: 0}); - const expectedOutput = [...Array.from({length: PARALLEL_COUNT + 1}, (_, index) => index), '.']; - t.deepEqual(ipcOutput, expectedOutput); -}; - -test('Multiple parallel exports.sendMessage() + exports.getOneMessage()', testSendHoldSubprocess, false, false); -test('Multiple parallel exports.sendMessage() + exports.getOneMessage(), filter', testSendHoldSubprocess, true, false); -test('Multiple parallel exports.sendMessage() + exports.getEachMessage()', testSendHoldSubprocess, false, true); - -const testSendHoldParentSerial = async (t, getMessage, buffer, filter) => { - const subprocess = execa('ipc-iterate.js', {ipc: true, buffer}); - - await subprocess.sendMessage(0); - t.is(await subprocess.getOneMessage({filter}), 0); - - const promise = subprocess.sendMessage(1); - subprocess.emit('message', '.'); - await promise; - - const messages = Array.from({length: PARALLEL_COUNT}, (_, index) => index + 2); - for (const message of messages) { - // eslint-disable-next-line no-await-in-loop - await subprocess.sendMessage(message); - } - - await subprocess.sendMessage(foobarString); - - const {ipcOutput} = await subprocess; - if (buffer) { - const expectedOutput = [0, '.', 1, ...messages]; - t.deepEqual(ipcOutput, expectedOutput); - } -}; - -test('Multiple serial subprocess.sendMessage() + subprocess.getOneMessage(), buffer false', testSendHoldParentSerial, subprocessGetOne, false, undefined); -test('Multiple serial subprocess.sendMessage() + subprocess.getOneMessage(), buffer true', testSendHoldParentSerial, subprocessGetOne, true, undefined); -test('Multiple serial subprocess.sendMessage() + subprocess.getOneMessage(), buffer false, filter', testSendHoldParentSerial, subprocessGetOne, false, alwaysPass); -test('Multiple serial subprocess.sendMessage() + subprocess.getOneMessage(), buffer true, filter', testSendHoldParentSerial, subprocessGetOne, true, alwaysPass); -test('Multiple serial subprocess.sendMessage() + subprocess.getEachMessage(), buffer false', testSendHoldParentSerial, subprocessGetFirst, false, undefined); -test('Multiple serial subprocess.sendMessage() + subprocess.getEachMessage(), buffer true', testSendHoldParentSerial, subprocessGetFirst, true, undefined); - -const testSendHoldSubprocessSerial = async (t, filter, isGetEach) => { - const {ipcOutput} = await execa('ipc-iterate-back-serial.js', [`${filter}`, `${isGetEach}`], {ipcInput: 0}); - const expectedOutput = [...Array.from({length: PARALLEL_COUNT + 2}, (_, index) => index), '.']; - t.deepEqual(ipcOutput, expectedOutput); -}; - -test('Multiple serial exports.sendMessage() + exports.getOneMessage()', testSendHoldSubprocessSerial, false, false); -test('Multiple serial exports.sendMessage() + exports.getOneMessage(), filter', testSendHoldSubprocessSerial, true, false); -test('Multiple serial exports.sendMessage() + exports.getEachMessage()', testSendHoldSubprocessSerial, false, true); diff --git a/test/ipc/pending.js b/test/ipc/pending.js deleted file mode 100644 index 0d3091179c..0000000000 --- a/test/ipc/pending.js +++ /dev/null @@ -1,87 +0,0 @@ -import {once} from 'node:events'; -import {setTimeout} from 'node:timers/promises'; -import test from 'ava'; -import {execa} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString} from '../helpers/input.js'; - -setFixtureDirectory(); - -const testBufferInitial = async (t, buffer) => { - const subprocess = execa('ipc-echo-wait.js', {buffer, ipcInput: foobarString}); - t.is(await subprocess.getOneMessage(), foobarString); - - const {ipcOutput} = await subprocess; - t.deepEqual(ipcOutput, buffer ? [foobarString] : []); -}; - -test('Buffers initial message to subprocess, buffer false', testBufferInitial, false); -test('Buffers initial message to subprocess, buffer true', testBufferInitial, true); - -const testBufferInitialSend = async (t, buffer) => { - const subprocess = execa('ipc-send-echo-wait.js', {buffer, ipcInput: foobarString}); - t.is(await subprocess.getOneMessage(), '.'); - t.is(await subprocess.getOneMessage(), foobarString); - - const {ipcOutput} = await subprocess; - t.deepEqual(ipcOutput, buffer ? ['.', foobarString] : []); -}; - -test('sendMessage() does not empty the initial message buffering, buffer false', testBufferInitialSend, false); -test('sendMessage() does not empty the initial message buffering, buffer true', testBufferInitialSend, true); - -const testBufferInitialStrict = async (t, buffer) => { - const subprocess = execa('ipc-send-echo-strict.js', {buffer, ipcInput: foobarString}); - t.is(await subprocess.getOneMessage(), '.'); - await setTimeout(1e3); - const promise = subprocess.getOneMessage(); - await subprocess.sendMessage('..'); - t.is(await promise, '..'); - - const {ipcOutput} = await subprocess; - t.deepEqual(ipcOutput, buffer ? ['.', '..'] : []); -}; - -test('sendMessage() with "strict" empties the initial message buffering, buffer false', testBufferInitialStrict, false); -test('sendMessage() with "strict" empties the initial message buffering, buffer true', testBufferInitialStrict, true); - -const testNoBufferInitial = async (t, buffer) => { - const subprocess = execa('ipc-send-print.js', {ipc: true, buffer}); - const [chunk] = await once(subprocess.stdout, 'data'); - t.is(chunk.toString(), '.'); - await setTimeout(1e3); - t.is(await Promise.race([setTimeout(0), subprocess.getOneMessage()]), undefined); - await subprocess.sendMessage('.'); - - const {ipcOutput} = await subprocess; - t.deepEqual(ipcOutput, buffer ? [foobarString] : []); -}; - -test.serial('Does not buffer initial message to current process, buffer false', testNoBufferInitial, false); -test.serial('Does not buffer initial message to current process, buffer true', testNoBufferInitial, true); - -const testReplay = async (t, buffer) => { - const subprocess = execa('ipc-replay.js', {buffer, ipcInput: foobarString}); - t.is(await subprocess.getOneMessage(), foobarString); - await subprocess.sendMessage('.'); - await setTimeout(2e3); - await subprocess.sendMessage(foobarString); - t.is(await subprocess.getOneMessage(), foobarString); - - const {ipcOutput} = await subprocess; - t.deepEqual(ipcOutput, buffer ? [foobarString, foobarString] : []); -}; - -test.serial('Does not replay missed messages in subprocess, buffer false', testReplay, false); -test.serial('Does not replay missed messages in subprocess, buffer true', testReplay, true); - -const testFastSend = async (t, buffer) => { - const subprocess = execa('ipc-send-native.js', {ipc: true, buffer}); - t.is(await subprocess.getOneMessage(), '.'); - - const {ipcOutput} = await subprocess; - t.deepEqual(ipcOutput, buffer ? ['.'] : []); -}; - -test('Subprocess can send messages right away, buffer false', testFastSend, false); -test('Subprocess can send messages right away, buffer true', testFastSend, true); diff --git a/test/ipc/reference.js b/test/ipc/reference.js deleted file mode 100644 index 01969a591c..0000000000 --- a/test/ipc/reference.js +++ /dev/null @@ -1,129 +0,0 @@ -import test from 'ava'; -import {execa} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString} from '../helpers/input.js'; -import {PARALLEL_COUNT} from '../helpers/parallel.js'; - -setFixtureDirectory(); - -const testReference = async (t, fixtureName) => { - const {timedOut} = await t.throwsAsync(execa(fixtureName, {ipc: true, timeout: 1e3})); - t.true(timedOut); -}; - -test('exports.getOneMessage() keeps the subprocess alive', testReference, 'ipc-get-ref.js'); -test('exports.getEachMessage() keeps the subprocess alive', testReference, 'ipc-iterate-ref.js'); - -const testUnreference = async (t, fixtureName) => { - const {ipcOutput} = await execa(fixtureName, {ipc: true}); - t.deepEqual(ipcOutput, []); -}; - -test('exports.getOneMessage() does not keep the subprocess alive, reference false', testUnreference, 'ipc-get-unref.js'); -test('exports.getEachMessage() does not keep the subprocess alive, reference false', testUnreference, 'ipc-iterate-unref.js'); - -test('exports.sendMessage() keeps the subprocess alive', async t => { - const {ipcOutput} = await execa('ipc-send-repeat.js', [`${PARALLEL_COUNT}`], {ipc: true}); - const expectedOutput = Array.from({length: PARALLEL_COUNT}, (_, index) => index); - t.deepEqual(ipcOutput, expectedOutput); -}); - -test('process.send() keeps the subprocess alive', async t => { - const {ipcOutput, stdout} = await execa('ipc-process-send.js', {ipc: true}); - t.deepEqual(ipcOutput, [foobarString]); - t.is(stdout, '.'); -}); - -test('process.send() keeps the subprocess alive, after getOneMessage()', async t => { - const {ipcOutput, stdout} = await execa('ipc-process-send-get.js', {ipcInput: 0}); - t.deepEqual(ipcOutput, [foobarString]); - t.is(stdout, '.'); -}); - -test('process.send() keeps the subprocess alive, after sendMessage()', async t => { - const {ipcOutput, stdout} = await execa('ipc-process-send-send.js', {ipc: true}); - t.deepEqual(ipcOutput, ['.', foobarString]); - t.is(stdout, '.'); -}); - -test('process.once("message") keeps the subprocess alive', async t => { - const subprocess = execa('ipc-once-message.js', {ipc: true}); - t.is(await subprocess.getOneMessage(), '.'); - await subprocess.sendMessage(foobarString); - - const {ipcOutput, stdout} = await subprocess; - t.deepEqual(ipcOutput, ['.']); - t.is(stdout, foobarString); -}); - -test('process.once("message") keeps the subprocess alive, after sendMessage()', async t => { - const subprocess = execa('ipc-once-message-send.js', {ipc: true}); - t.is(await subprocess.getOneMessage(), '.'); - await subprocess.sendMessage(foobarString); - - const {ipcOutput, stdout} = await subprocess; - t.deepEqual(ipcOutput, ['.']); - t.is(stdout, foobarString); -}); - -test('process.once("message") keeps the subprocess alive, after getOneMessage()', async t => { - const subprocess = execa('ipc-once-message-get.js', {ipc: true}); - await subprocess.sendMessage('.'); - t.is(await subprocess.getOneMessage(), '.'); - await subprocess.sendMessage(foobarString); - - const {ipcOutput, stdout} = await subprocess; - t.deepEqual(ipcOutput, ['.']); - t.is(stdout, foobarString); -}); - -test('process.once("disconnect") keeps the subprocess alive', async t => { - const subprocess = execa('ipc-once-disconnect.js', {ipc: true}); - t.is(await subprocess.getOneMessage(), '.'); - subprocess.disconnect(); - - const {ipcOutput, stdout} = await subprocess; - t.deepEqual(ipcOutput, ['.']); - t.is(stdout, '.'); -}); - -test('process.once("disconnect") keeps the subprocess alive, after sendMessage()', async t => { - const subprocess = execa('ipc-once-disconnect-send.js', {ipc: true}); - t.is(await subprocess.getOneMessage(), '.'); - t.is(await subprocess.getOneMessage(), '.'); - subprocess.disconnect(); - - const {ipcOutput, stdout} = await subprocess; - t.deepEqual(ipcOutput, ['.', '.']); - t.is(stdout, '.'); -}); - -test('process.once("disconnect") does not keep the subprocess alive, after getOneMessage()', async t => { - const subprocess = execa('ipc-once-disconnect-get.js', {ipc: true}); - await subprocess.sendMessage('.'); - t.is(await subprocess.getOneMessage(), '.'); - subprocess.disconnect(); - - const {ipcOutput, stdout} = await subprocess; - t.deepEqual(ipcOutput, ['.']); - t.is(stdout, '.'); -}); - -test('Can call subprocess.disconnect() right away', async t => { - const subprocess = execa('ipc-send.js', {ipc: true}); - subprocess.disconnect(); - t.is(subprocess.channel, null); - - await t.throwsAsync(subprocess.getOneMessage(), { - message: /subprocess.getOneMessage\(\) could not complete/, - }); - await t.throwsAsync(subprocess, { - message: /Error: sendMessage\(\) cannot be used/, - }); -}); - -test('Can call process.disconnect() right away', async t => { - const {stdout, stderr} = await t.throwsAsync(execa('ipc-disconnect-get.js', {ipc: true})); - t.is(stdout, 'null'); - t.true(stderr.includes('Error: getOneMessage() cannot be used')); -}); diff --git a/test/ipc/send.js b/test/ipc/send.js deleted file mode 100644 index 8075b3af1e..0000000000 --- a/test/ipc/send.js +++ /dev/null @@ -1,146 +0,0 @@ -import test from 'ava'; -import {execa} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString} from '../helpers/input.js'; -import {PARALLEL_COUNT} from '../helpers/parallel.js'; -import {mockSendIoError} from '../helpers/ipc.js'; - -setFixtureDirectory(); - -test('Can exchange IPC messages', async t => { - const subprocess = execa('ipc-echo.js', {ipc: true}); - await subprocess.sendMessage(foobarString); - t.is(await subprocess.getOneMessage(), foobarString); - await subprocess; -}); - -test.serial('Can exchange IPC messages under heavy load', async t => { - await Promise.all( - Array.from({length: PARALLEL_COUNT}, async (_, index) => { - const subprocess = execa('ipc-echo.js', {ipc: true}); - await subprocess.sendMessage(index); - t.is(await subprocess.getOneMessage(), index); - await subprocess; - }), - ); -}); - -test('The "serialization" option defaults to "advanced"', async t => { - const subprocess = execa('ipc-echo.js', {ipc: true}); - await subprocess.sendMessage([0n]); - const message = await subprocess.getOneMessage(); - t.is(message[0], 0n); - await subprocess; -}); - -test('Can use "serialization: json" option', async t => { - const subprocess = execa('ipc-echo.js', {ipc: true, serialization: 'json'}); - const date = new Date(); - await subprocess.sendMessage(date); - t.is(await subprocess.getOneMessage(), date.toJSON()); - await subprocess; -}); - -test('Validates JSON payload with serialization: "json"', async t => { - const subprocess = execa('ipc-echo.js', {ipc: true, serialization: 'json'}); - await t.throwsAsync(subprocess.sendMessage([0n]), {message: /serialize a BigInt/}); - await t.throwsAsync(subprocess); -}); - -const BIG_PAYLOAD_SIZE = '.'.repeat(1e6); - -test('Handles backpressure', async t => { - const subprocess = execa('ipc-iterate.js', {ipc: true}); - await subprocess.sendMessage(BIG_PAYLOAD_SIZE); - t.true(subprocess.send(foobarString)); - t.is(await subprocess.getOneMessage(), BIG_PAYLOAD_SIZE); - const {ipcOutput} = await subprocess; - t.deepEqual(ipcOutput, [BIG_PAYLOAD_SIZE]); -}); - -test('Disconnects IPC on exports.sendMessage() error', async t => { - const subprocess = execa('ipc-get-send-get.js', ['false'], {ipc: true}); - await subprocess.sendMessage(foobarString); - t.is(await subprocess.getOneMessage(), foobarString); - - const {message, cause} = await t.throwsAsync(subprocess.sendMessage(0n)); - t.is(message, 'subprocess.sendMessage()\'s argument type is invalid: the message cannot be serialized: 0.'); - t.true(cause.message.includes('The "message" argument must be one of type string')); - - const {exitCode, isTerminated, stderr} = await t.throwsAsync(subprocess); - t.is(exitCode, 1); - t.false(isTerminated); - t.true(stderr.includes('Error: getOneMessage() could not complete')); -}); - -test('Disconnects IPC on subprocess.sendMessage() error', async t => { - const subprocess = execa('ipc-send-error.js', {ipc: true}); - const ipcError = await t.throwsAsync(subprocess.getOneMessage()); - t.true(ipcError.message.includes('subprocess.getOneMessage() could not complete')); - - const {exitCode, isTerminated, stderr} = await t.throwsAsync(subprocess); - t.is(exitCode, 1); - t.false(isTerminated); - t.true(stderr.includes('Error: sendMessage()\'s argument type is invalid: the message cannot be serialized: 0.')); - t.true(stderr.includes('The "message" argument must be one of type string')); -}); - -// EPIPE happens based on timing conditions, so we must repeat it until it happens -const findEpipeError = async t => { - // eslint-disable-next-line no-constant-condition - while (true) { - // eslint-disable-next-line no-await-in-loop - const error = await t.throwsAsync(getEpipeError()); - if (error.cause?.code === 'EPIPE') { - return error; - } - } -}; - -const getEpipeError = async () => { - const subprocess = execa('empty.js', {ipc: true}); - // eslint-disable-next-line no-constant-condition - while (true) { - // eslint-disable-next-line no-await-in-loop - await subprocess.sendMessage('.'); - } -}; - -test.serial('Can send messages while the subprocess is closing', async t => { - const {message} = await findEpipeError(t); - t.is(message, 'subprocess.sendMessage() cannot be used: the subprocess is disconnecting.'); -}); - -test('subprocess.sendMessage() handles I/O errors', async t => { - const subprocess = execa('ipc-echo.js', {ipc: true}); - const error = mockSendIoError(subprocess); - t.is(await t.throwsAsync(subprocess.sendMessage('.')), error); - - const {exitCode, isTerminated, message, ipcOutput} = await t.throwsAsync(subprocess); - t.is(exitCode, 1); - t.false(isTerminated); - t.true(message.includes('Error: getOneMessage() cannot be used: the parent process has already exited or disconnected')); - t.deepEqual(ipcOutput, []); -}); - -test('Does not hold message events on I/O errors', async t => { - const subprocess = execa('ipc-echo.js', {ipc: true}); - const error = mockSendIoError(subprocess); - const promise = subprocess.sendMessage('.'); - subprocess.emit('message', '.'); - t.is(await t.throwsAsync(promise), error); - - const {exitCode, isTerminated, message, ipcOutput} = await t.throwsAsync(subprocess); - t.is(exitCode, 1); - t.false(isTerminated); - t.true(message.includes('Error: getOneMessage() cannot be used: the parent process has already exited or disconnected')); - t.deepEqual(ipcOutput, ['.']); -}); - -test('exports.sendMessage() handles I/O errors', async t => { - const {exitCode, isTerminated, message, ipcOutput} = await t.throwsAsync(execa('ipc-send-io-error.js', {ipc: true})); - t.is(exitCode, 1); - t.false(isTerminated); - t.true(message.includes(`Error: ${foobarString}`)); - t.deepEqual(ipcOutput, []); -}); diff --git a/test/ipc/strict.js b/test/ipc/strict.js deleted file mode 100644 index 42b3312e3d..0000000000 --- a/test/ipc/strict.js +++ /dev/null @@ -1,229 +0,0 @@ -import {once} from 'node:events'; -import test from 'ava'; -import {execa} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString} from '../helpers/input.js'; -import {assertMaxListeners} from '../helpers/listeners.js'; -import {subprocessGetOne, subprocessGetFirst, mockSendIoError} from '../helpers/ipc.js'; -import {PARALLEL_COUNT} from '../helpers/parallel.js'; - -setFixtureDirectory(); - -const testStrictSuccessParentOne = async (t, buffer) => { - const subprocess = execa('ipc-echo.js', {ipc: true, buffer}); - await subprocess.sendMessage(foobarString, {strict: true}); - t.is(await subprocess.getOneMessage(), foobarString); - - const {ipcOutput} = await subprocess; - t.deepEqual(ipcOutput, buffer ? [foobarString] : []); -}; - -test('subprocess.sendMessage() "strict" succeeds if the subprocess uses exports.getOneMessage(), buffer false', testStrictSuccessParentOne, false); -test('subprocess.sendMessage() "strict" succeeds if the subprocess uses exports.getOneMessage(), buffer true', testStrictSuccessParentOne, true); - -const testStrictSuccessParentEach = async (t, buffer) => { - const subprocess = execa('ipc-iterate.js', {ipc: true, buffer}); - await subprocess.sendMessage('.', {strict: true}); - t.is(await subprocess.getOneMessage(), '.'); - await subprocess.sendMessage(foobarString); - - const {ipcOutput} = await subprocess; - t.deepEqual(ipcOutput, buffer ? ['.'] : []); -}; - -test('subprocess.sendMessage() "strict" succeeds if the subprocess uses exports.getEachMessage(), buffer false', testStrictSuccessParentEach, false); -test('subprocess.sendMessage() "strict" succeeds if the subprocess uses exports.getEachMessage(), buffer true', testStrictSuccessParentEach, true); - -const testStrictMissingParent = async (t, buffer) => { - const subprocess = execa('ipc-echo-twice.js', {ipcInput: foobarString, buffer}); - const promise = subprocess.getOneMessage(); - const secondPromise = subprocess.sendMessage(foobarString, {strict: true}); - const {message} = await t.throwsAsync(subprocess.sendMessage(foobarString, {strict: true})); - t.is(message, 'subprocess.sendMessage() failed: the subprocess is not listening to incoming messages.'); - t.is(await promise, foobarString); - await secondPromise; - - const {ipcOutput} = await subprocess; - t.deepEqual(ipcOutput, buffer ? [foobarString, foobarString] : []); -}; - -test('subprocess.sendMessage() "strict" fails if the subprocess is not listening, buffer false', testStrictMissingParent, false); -test('subprocess.sendMessage() "strict" fails if the subprocess is not listening, buffer true', testStrictMissingParent, true); - -const testStrictExit = async (t, buffer) => { - const subprocess = execa('ipc-send.js', {ipc: true, buffer}); - const {message} = await t.throwsAsync(subprocess.sendMessage(foobarString, {strict: true})); - t.is(message, 'subprocess.sendMessage() failed: the subprocess exited without listening to incoming messages.'); - - const {ipcOutput} = await subprocess; - t.deepEqual(ipcOutput, buffer ? [foobarString] : []); -}; - -test('subprocess.sendMessage() "strict" fails if the subprocess exits, buffer false', testStrictExit, false); -test('subprocess.sendMessage() "strict" fails if the subprocess exits, buffer true', testStrictExit, true); - -const testStrictSuccessSubprocess = async (t, getMessage, buffer) => { - const subprocess = execa('ipc-send-strict.js', {ipc: true, buffer}); - t.is(await getMessage(subprocess), foobarString); - - const {ipcOutput} = await subprocess; - t.deepEqual(ipcOutput, buffer ? [foobarString] : []); -}; - -test('exports.sendMessage() "strict" succeeds if the current process uses subprocess.getOneMessage(), buffer false', testStrictSuccessSubprocess, subprocessGetOne, false); -test('exports.sendMessage() "strict" succeeds if the current process uses subprocess.getOneMessage(), buffer true', testStrictSuccessSubprocess, subprocessGetOne, true); -test('exports.sendMessage() "strict" succeeds if the current process uses subprocess.getEachMessage(), buffer false', testStrictSuccessSubprocess, subprocessGetFirst, false); -test('exports.sendMessage() "strict" succeeds if the current process uses subprocess.getEachMessage(), buffer true', testStrictSuccessSubprocess, subprocessGetFirst, true); - -test('exports.sendMessage() "strict" succeeds if the current process uses result.ipcOutput', async t => { - const {ipcOutput} = await execa('ipc-send-strict.js', {ipc: true}); - t.deepEqual(ipcOutput, [foobarString]); -}); - -test('exports.sendMessage() "strict" fails if the current process is not listening, buffer false', async t => { - const {exitCode, isTerminated, stderr, ipcOutput} = await t.throwsAsync(execa('ipc-send-strict.js', {ipc: true, buffer: {ipc: false}})); - t.is(exitCode, 1); - t.false(isTerminated); - t.true(stderr.includes('Error: sendMessage() failed: the parent process is not listening to incoming messages.')); - t.deepEqual(ipcOutput, []); -}); - -test.serial('Multiple subprocess.sendMessage() "strict" at once', async t => { - const checkMaxListeners = assertMaxListeners(t); - - const subprocess = execa('ipc-iterate.js', {ipc: true}); - const messages = Array.from({length: PARALLEL_COUNT}, (_, index) => index); - await Promise.all(messages.map(message => subprocess.sendMessage(message, {strict: true}))); - await subprocess.sendMessage(foobarString); - - const {ipcOutput} = await subprocess; - t.deepEqual(ipcOutput, messages); - - checkMaxListeners(); -}); - -test('subprocess.sendMessage() "strict" fails if the subprocess uses once()', async t => { - const subprocess = execa('ipc-once-message.js', {ipc: true}); - const {message} = await t.throwsAsync(subprocess.sendMessage(foobarString, {strict: true})); - t.is(message, 'subprocess.sendMessage() failed: the subprocess exited without listening to incoming messages.'); - - const {ipcOutput} = await subprocess; - t.deepEqual(ipcOutput, ['.']); -}); - -test('exports.sendMessage() "strict" fails if the current process uses once() and buffer false', async t => { - const subprocess = execa('ipc-send-strict.js', {ipc: true, buffer: {ipc: false}}); - const [message] = await once(subprocess, 'message'); - t.deepEqual(message, { - id: 0n, - type: 'execa:ipc:request', - message: foobarString, - hasListeners: false, - }); - - const {exitCode, isTerminated, stderr, ipcOutput} = await t.throwsAsync(subprocess); - t.is(exitCode, 1); - t.false(isTerminated); - t.true(stderr.includes('Error: sendMessage() failed: the parent process is not listening to incoming messages.')); - t.deepEqual(ipcOutput, []); -}); - -test('subprocess.sendMessage() "strict" failure disconnects', async t => { - const subprocess = execa('ipc-echo-twice-wait.js', {ipcInput: foobarString}); - const promise = subprocess.getOneMessage(); - const secondPromise = subprocess.sendMessage(foobarString, {strict: true}); - const {message} = await t.throwsAsync(subprocess.sendMessage(foobarString, {strict: true})); - t.is(message, 'subprocess.sendMessage() failed: the subprocess is not listening to incoming messages.'); - t.is(await promise, foobarString); - await secondPromise; - - const {exitCode, isTerminated, stderr, ipcOutput} = await t.throwsAsync(subprocess); - t.is(exitCode, 1); - t.false(isTerminated); - t.true(stderr.includes('Error: sendMessage() cannot be used: the parent process has already exited or disconnected.')); - t.deepEqual(ipcOutput, [foobarString, foobarString]); -}); - -test('exports.sendMessage() "strict" failure disconnects', async t => { - const {exitCode, isTerminated, stderr, ipcOutput} = await t.throwsAsync(execa('ipc-send-strict-catch.js', {ipc: true, buffer: {ipc: false}})); - t.is(exitCode, 1); - t.false(isTerminated); - t.true(stderr.includes('Error: sendMessage() cannot be used: the parent process has already exited or disconnected.')); - t.deepEqual(ipcOutput, []); -}); - -const testIoErrorParent = async (t, getMessage) => { - const subprocess = execa('ipc-send-strict.js', {ipc: true}); - const cause = mockSendIoError(subprocess); - const error = await t.throwsAsync(getMessage(subprocess)); - t.true(error.message.includes('subprocess.sendMessage() failed when sending an acknowledgment response to the subprocess.')); - t.is(getMessage === subprocessGetOne ? error.cause : error.cause.cause, cause); - - const {exitCode, isTerminated, stderr, ipcOutput} = await t.throwsAsync(subprocess); - t.is(exitCode, 1); - t.false(isTerminated); - t.true(stderr.includes('Error: sendMessage() failed: the parent process exited without listening to incoming messages.')); - t.deepEqual(ipcOutput, []); -}; - -test('subprocess.getOneMessage() acknowledgment I/O error', testIoErrorParent, subprocessGetOne); -test('subprocess.getEachMessage() acknowledgment I/O error', testIoErrorParent, subprocessGetFirst); - -const testIoErrorSubprocess = async (t, fixtureName) => { - const subprocess = execa(fixtureName, {ipc: true}); - const {message} = await t.throwsAsync(subprocess.sendMessage(foobarString, {strict: true})); - t.is(message, 'subprocess.sendMessage() failed: the subprocess exited without listening to incoming messages.'); - - const {exitCode, isTerminated, stdout, stderr, ipcOutput} = await t.throwsAsync(subprocess); - t.is(exitCode, 1); - t.false(isTerminated); - t.is(stdout, ''); - t.true(stderr.includes('Error: sendMessage() failed when sending an acknowledgment response to the parent process.')); - t.true(stderr.includes(`Error: ${foobarString}`)); - t.deepEqual(ipcOutput, []); -}; - -test('exports.getOneMessage() acknowledgment I/O error', testIoErrorSubprocess, 'ipc-get-io-error.js'); -test('exports.getEachMessage() acknowledgment I/O error', testIoErrorSubprocess, 'ipc-iterate-io-error.js'); - -test('Opposite sendMessage() "strict", buffer true', async t => { - const subprocess = execa('ipc-send-strict-get.js', {ipc: true}); - await subprocess.sendMessage(foobarString, {strict: true}); - - const {ipcOutput} = await subprocess; - t.deepEqual(ipcOutput, [foobarString, foobarString]); -}); - -test('Opposite sendMessage() "strict", current process listening, buffer false', async t => { - const subprocess = execa('ipc-send-strict-get.js', {ipc: true, buffer: {ipc: false}}); - const [message] = await Promise.all([ - subprocess.getOneMessage(), - subprocess.sendMessage(foobarString, {strict: true}), - ]); - t.is(message, foobarString); - t.is(await subprocess.getOneMessage(), foobarString); - - const {ipcOutput} = await subprocess; - t.deepEqual(ipcOutput, []); -}); - -test('Opposite sendMessage() "strict", subprocess listening, buffer false', async t => { - const subprocess = execa('ipc-send-strict-listen.js', {ipc: true, buffer: {ipc: false}}); - await subprocess.sendMessage(foobarString, {strict: true}); - t.is(await subprocess.getOneMessage(), foobarString); - - const {ipcOutput} = await subprocess; - t.deepEqual(ipcOutput, []); -}); - -test('Opposite sendMessage() "strict", not listening, buffer false', async t => { - const subprocess = execa('ipc-send-strict.js', {ipc: true, buffer: {ipc: false}}); - const {message} = await t.throwsAsync(subprocess.sendMessage(foobarString, {strict: true})); - t.true(message.startsWith('subprocess.sendMessage() failed: the subprocess is sending a message too, instead of listening to incoming messages.')); - - const {exitCode, isTerminated, stderr, ipcOutput} = await t.throwsAsync(subprocess); - t.is(exitCode, 1); - t.false(isTerminated); - t.true(stderr.includes('Error: sendMessage() failed: the parent process is sending a message too, instead of listening to incoming messages.')); - t.deepEqual(ipcOutput, []); -}); diff --git a/test/ipc/validation.js b/test/ipc/validation.js deleted file mode 100644 index b7e4d72a52..0000000000 --- a/test/ipc/validation.js +++ /dev/null @@ -1,100 +0,0 @@ -import test from 'ava'; -import {execa} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString} from '../helpers/input.js'; -import {getStdio} from '../helpers/stdio.js'; - -setFixtureDirectory(); - -const stdioIpc = getStdio(3, 'ipc'); - -const testRequiredIpcSubprocess = async (t, methodName, options) => { - const subprocess = execa('empty.js', options); - const {message} = await t.throws(() => subprocess[methodName]()); - t.true(message.includes(`subprocess.${methodName}() can only be used`)); - await subprocess; -}; - -test('Cannot use subprocess.sendMessage() without ipc option', testRequiredIpcSubprocess, 'sendMessage', {}); -test('Cannot use subprocess.sendMessage() with ipc: false', testRequiredIpcSubprocess, 'sendMessage', {ipc: false}); -test('Cannot use subprocess.sendMessage() with stdio: [..., "ipc"]', testRequiredIpcSubprocess, 'sendMessage', stdioIpc); -test('Cannot use subprocess.getOneMessage() without ipc option', testRequiredIpcSubprocess, 'getOneMessage', {}); -test('Cannot use subprocess.getOneMessage() with ipc: false', testRequiredIpcSubprocess, 'getOneMessage', {ipc: false}); -test('Cannot use subprocess.getOneMessage() with stdio: [..., "ipc"]', testRequiredIpcSubprocess, 'getOneMessage', stdioIpc); -test('Cannot use subprocess.getEachMessage() without ipc option', testRequiredIpcSubprocess, 'getEachMessage', {}); -test('Cannot use subprocess.getEachMessage() with ipc: false', testRequiredIpcSubprocess, 'getEachMessage', {ipc: false}); -test('Cannot use subprocess.getEachMessage() with stdio: [..., "ipc"]', testRequiredIpcSubprocess, 'getEachMessage', stdioIpc); - -const testRequiredIpcExports = async (t, methodName, options) => { - const {message} = await t.throwsAsync(execa('ipc-any.js', [methodName], options)); - t.true(message.includes(`${methodName}() can only be used`)); -}; - -test('Cannot use exports.sendMessage() without ipc option', testRequiredIpcExports, 'sendMessage', {}); -test('Cannot use exports.sendMessage() with ipc: false', testRequiredIpcExports, 'sendMessage', {ipc: false}); -test('Cannot use exports.getOneMessage() without ipc option', testRequiredIpcExports, 'getOneMessage', {}); -test('Cannot use exports.getOneMessage() with ipc: false', testRequiredIpcExports, 'getOneMessage', {ipc: false}); -test('Cannot use exports.getEachMessage() without ipc option', testRequiredIpcExports, 'getEachMessage', {}); -test('Cannot use exports.getEachMessage() with ipc: false', testRequiredIpcExports, 'getEachMessage', {ipc: false}); - -const testPostDisconnection = async (t, methodName) => { - const subprocess = execa('empty.js', {ipc: true}); - await subprocess; - const {message} = t.throws(() => subprocess[methodName](foobarString)); - t.true(message.includes(`subprocess.${methodName}() cannot be used`)); -}; - -test('subprocess.sendMessage() after disconnection', testPostDisconnection, 'sendMessage'); -test('subprocess.getOneMessage() after disconnection', testPostDisconnection, 'getOneMessage'); -test('subprocess.getEachMessage() after disconnection', testPostDisconnection, 'getEachMessage'); - -const testPostDisconnectionSubprocess = async (t, methodName) => { - const subprocess = execa('ipc-disconnect.js', [methodName], {ipc: true}); - subprocess.disconnect(); - const {message} = await t.throwsAsync(subprocess); - t.true(message.includes(`${methodName}() cannot be used`)); -}; - -test('exports.sendMessage() after disconnection', testPostDisconnectionSubprocess, 'sendMessage'); -test('exports.getOneMessage() after disconnection', testPostDisconnectionSubprocess, 'getOneMessage'); -test('exports.getEachMessage() after disconnection', testPostDisconnectionSubprocess, 'getEachMessage'); - -const INVALID_TYPE_MESSAGE = 'The "message" argument must be one of type string'; -const UNDEFINED_MESSAGE = 'The "message" argument must be specified'; -const CLONE_MESSAGE = 'could not be cloned'; -const CYCLE_MESSAGE = 'Converting circular structure to JSON'; -const MAX_CALL_STACK_MESSAGE = 'Maximum call stack size exceeded'; - -const testInvalidPayload = async (t, serialization, message, expectedMessage) => { - const subprocess = execa('empty.js', {ipc: true, serialization}); - const error = await t.throwsAsync(subprocess.sendMessage(message)); - t.true(error.message.includes('subprocess.sendMessage()\'s argument type is invalid: the message cannot be serialized')); - t.true(error.cause.message.includes(expectedMessage)); - await subprocess; -}; - -const cycleObject = {}; -cycleObject.self = cycleObject; -const toJsonCycle = {toJSON: () => ({test: true, toJsonCycle})}; - -test('subprocess.sendMessage() cannot send undefined', testInvalidPayload, 'advanced', undefined, UNDEFINED_MESSAGE); -test('subprocess.sendMessage() cannot send bigints', testInvalidPayload, 'advanced', 0n, INVALID_TYPE_MESSAGE); -test('subprocess.sendMessage() cannot send symbols', testInvalidPayload, 'advanced', Symbol('test'), INVALID_TYPE_MESSAGE); -test('subprocess.sendMessage() cannot send functions', testInvalidPayload, 'advanced', () => {}, INVALID_TYPE_MESSAGE); -test('subprocess.sendMessage() cannot send promises', testInvalidPayload, 'advanced', Promise.resolve(), CLONE_MESSAGE); -test('subprocess.sendMessage() cannot send proxies', testInvalidPayload, 'advanced', new Proxy({}, {}), CLONE_MESSAGE); -test('subprocess.sendMessage() cannot send Intl', testInvalidPayload, 'advanced', new Intl.Collator(), CLONE_MESSAGE); -test('subprocess.sendMessage() cannot send undefined, JSON', testInvalidPayload, 'json', undefined, UNDEFINED_MESSAGE); -test('subprocess.sendMessage() cannot send bigints, JSON', testInvalidPayload, 'json', 0n, INVALID_TYPE_MESSAGE); -test('subprocess.sendMessage() cannot send symbols, JSON', testInvalidPayload, 'json', Symbol('test'), INVALID_TYPE_MESSAGE); -test('subprocess.sendMessage() cannot send functions, JSON', testInvalidPayload, 'json', () => {}, INVALID_TYPE_MESSAGE); -test('subprocess.sendMessage() cannot send cycles, JSON', testInvalidPayload, 'json', cycleObject, CYCLE_MESSAGE); -test('subprocess.sendMessage() cannot send cycles in toJSON(), JSON', testInvalidPayload, 'json', toJsonCycle, MAX_CALL_STACK_MESSAGE); - -test('exports.sendMessage() validates payload', async t => { - const subprocess = execa('ipc-echo-item.js', {ipc: true}); - await subprocess.sendMessage([undefined]); - const {message} = await t.throwsAsync(subprocess); - t.true(message.includes('Error: sendMessage()\'s argument type is invalid: the message cannot be serialized')); - t.true(message.includes(UNDEFINED_MESSAGE)); -}); diff --git a/test/methods/bind.js b/test/methods/bind.js deleted file mode 100644 index e382935960..0000000000 --- a/test/methods/bind.js +++ /dev/null @@ -1,109 +0,0 @@ -import path from 'node:path'; -import test from 'ava'; -import { - execa, - execaSync, - execaNode, - $, -} from '../../index.js'; -import {foobarString, foobarUppercase} from '../helpers/input.js'; -import {uppercaseGenerator} from '../helpers/generator.js'; -import {setFixtureDirectory, FIXTURES_DIRECTORY} from '../helpers/fixtures-directory.js'; - -setFixtureDirectory(); - -const NOOP_PATH = path.join(FIXTURES_DIRECTORY, 'noop.js'); -const PRINT_ENV_PATH = path.join(FIXTURES_DIRECTORY, 'environment.js'); - -const testBindOptions = async (t, execaMethod) => { - const {stdout} = await execaMethod({stripFinalNewline: false})(NOOP_PATH, [foobarString]); - t.is(stdout, `${foobarString}\n`); -}; - -test('execa() can bind options', testBindOptions, execa); -test('execaNode() can bind options', testBindOptions, execaNode); -test('$ can bind options', testBindOptions, $); - -const testBindOptionsSync = (t, execaMethod) => { - const {stdout} = execaMethod({stripFinalNewline: false})(NOOP_PATH, [foobarString]); - t.is(stdout, `${foobarString}\n`); -}; - -test('execaSync() can bind options', testBindOptionsSync, execaSync); -test('$.sync can bind options', testBindOptionsSync, $.sync); - -const testBindPriority = async (t, execaMethod) => { - const {stdout} = await execaMethod({stripFinalNewline: false})(NOOP_PATH, [foobarString], {stripFinalNewline: true}); - t.is(stdout, foobarString); -}; - -test('execa() bound options have lower priority', testBindPriority, execa); -test('execaSync() bound options have lower priority', testBindPriority, execaSync); -test('execaNode() bound options have lower priority', testBindPriority, execaNode); -test('$ bound options have lower priority', testBindPriority, $); -test('$.sync bound options have lower priority', testBindPriority, $.sync); - -const testBindUndefined = async (t, execaMethod) => { - const {stdout} = await execaMethod({stripFinalNewline: false})(NOOP_PATH, [foobarString], {stripFinalNewline: undefined}); - t.is(stdout, foobarString); -}; - -test('execa() undefined options use default value', testBindUndefined, execa); -test('execaSync() undefined options use default value', testBindUndefined, execaSync); -test('execaNode() undefined options use default value', testBindUndefined, execaNode); -test('$ undefined options use default value', testBindUndefined, $); -test('$.sync undefined options use default value', testBindUndefined, $.sync); - -const testMergeEnv = async (t, execaMethod) => { - const {stdout} = await execaMethod({env: {FOO: 'foo'}})(PRINT_ENV_PATH, {env: {BAR: 'bar'}}); - t.is(stdout, 'foo\nbar'); -}; - -test('execa() bound options are merged', testMergeEnv, execa); -test('execaSync() bound options are merged', testMergeEnv, execaSync); -test('execaNode() bound options are merged', testMergeEnv, execaNode); -test('$ bound options are merged', testMergeEnv, $); -test('$.sync bound options are merged', testMergeEnv, $.sync); - -const testMergeMultiple = async (t, execaMethod) => { - const {stdout} = await execaMethod({env: {FOO: 'baz'}})({env: {BAR: 'bar'}})(PRINT_ENV_PATH, {env: {FOO: 'foo'}}); - t.is(stdout, 'foo\nbar'); -}; - -test('execa() bound options are merged multiple times', testMergeMultiple, execa); -test('execaSync() bound options are merged multiple times', testMergeMultiple, execaSync); -test('execaNode() bound options are merged multiple times', testMergeMultiple, execaNode); -test('$ bound options are merged multiple times', testMergeMultiple, $); -test('$.sync bound options are merged multiple times', testMergeMultiple, $.sync); - -const testMergeFdSpecific = async (t, execaMethod) => { - const {isMaxBuffer, shortMessage} = await t.throwsAsync(execaMethod({maxBuffer: {stdout: 1}})(NOOP_PATH, [foobarString], {maxBuffer: {stderr: 100}})); - t.true(isMaxBuffer); - t.true(shortMessage.includes('Command\'s stdout was larger than 1')); -}; - -test('execa() bound options merge fd-specific ones', testMergeFdSpecific, execa); -test('execaNode() bound options merge fd-specific ones', testMergeFdSpecific, execaNode); -test('$ bound options merge fd-specific ones', testMergeFdSpecific, $); - -const testMergeEnvUndefined = async (t, execaMethod) => { - const {stdout} = await execaMethod({env: {FOO: 'foo'}})(PRINT_ENV_PATH, {env: {BAR: undefined}}); - t.is(stdout, 'foo\nundefined'); -}; - -test('execa() bound options are merged even if undefined', testMergeEnvUndefined, execa); -test('execaSync() bound options are merged even if undefined', testMergeEnvUndefined, execaSync); -test('execaNode() bound options are merged even if undefined', testMergeEnvUndefined, execaNode); -test('$ bound options are merged even if undefined', testMergeEnvUndefined, $); -test('$.sync bound options are merged even if undefined', testMergeEnvUndefined, $.sync); - -const testMergeSpecific = async (t, execaMethod) => { - const {stdout} = await execaMethod({stdout: {transform: uppercaseGenerator().transform, objectMode: true}})(NOOP_PATH, {stdout: {transform: uppercaseGenerator().transform}}); - t.is(stdout, foobarUppercase); -}; - -test('execa() bound options only merge specific ones', testMergeSpecific, execa); -test('execaSync() bound options only merge specific ones', testMergeSpecific, execaSync); -test('execaNode() bound options only merge specific ones', testMergeSpecific, execaNode); -test('$ bound options only merge specific ones', testMergeSpecific, $); -test('$.sync bound options only merge specific ones', testMergeSpecific, $.sync); diff --git a/test/methods/command.js b/test/methods/command.js deleted file mode 100644 index e68417a1ea..0000000000 --- a/test/methods/command.js +++ /dev/null @@ -1,199 +0,0 @@ -import path from 'node:path'; -import test from 'ava'; -import { - execa, - execaSync, - $, - execaNode, - execaCommand, - execaCommandSync, - parseCommandString, -} from '../../index.js'; -import { - setFixtureDirectory, - FIXTURES_DIRECTORY, - FIXTURES_DIRECTORY_URL, -} from '../helpers/fixtures-directory.js'; -import {QUOTE} from '../helpers/verbose.js'; - -setFixtureDirectory(); -const STDIN_FIXTURE = path.join(FIXTURES_DIRECTORY, 'stdin.js'); -const ECHO_FIXTURE_URL = new URL('echo.js', FIXTURES_DIRECTORY_URL); - -const parseAndRunCommand = command => execa`${parseCommandString(command)}`; - -test('execaCommand()', async t => { - const {stdout} = await execaCommand('echo.js foo bar'); - t.is(stdout, 'foo\nbar'); -}); - -test('parseCommandString() + execa()', async t => { - const {stdout} = await execa('echo.js', parseCommandString('foo bar')); - t.is(stdout, 'foo\nbar'); -}); - -test('execaCommandSync()', t => { - const {stdout} = execaCommandSync('echo.js foo bar'); - t.is(stdout, 'foo\nbar'); -}); - -test('parseCommandString() + execaSync()', t => { - const {stdout} = execaSync('echo.js', parseCommandString('foo bar')); - t.is(stdout, 'foo\nbar'); -}); - -test('execaCommand`...`', async t => { - const {stdout} = await execaCommand`${'echo.js foo bar'}`; - t.is(stdout, 'foo\nbar'); -}); - -test('parseCommandString() + execa`...`', async t => { - const {stdout} = await execa`${parseCommandString('echo.js foo bar')}`; - t.is(stdout, 'foo\nbar'); -}); - -test('parseCommandString() + execa`...`, only arguments', async t => { - const {stdout} = await execa`echo.js ${parseCommandString('foo bar')}`; - t.is(stdout, 'foo\nbar'); -}); - -test('parseCommandString() + execa`...`, only some arguments', async t => { - const {stdout} = await execa`echo.js ${'foo bar'} ${parseCommandString('foo bar')}`; - t.is(stdout, 'foo bar\nfoo\nbar'); -}); - -test('execaCommandSync`...`', t => { - const {stdout} = execaCommandSync`${'echo.js foo bar'}`; - t.is(stdout, 'foo\nbar'); -}); - -test('parseCommandString() + execaSync`...`', t => { - const {stdout} = execaSync`${parseCommandString('echo.js foo bar')}`; - t.is(stdout, 'foo\nbar'); -}); - -test('parseCommandString() + execaSync`...`, only arguments', t => { - const {stdout} = execaSync`echo.js ${parseCommandString('foo bar')}`; - t.is(stdout, 'foo\nbar'); -}); - -test('parseCommandString() + execaSync`...`, only some arguments', t => { - const {stdout} = execaSync`echo.js ${'foo bar'} ${parseCommandString('foo bar')}`; - t.is(stdout, 'foo bar\nfoo\nbar'); -}); - -test('parseCommandString() + $', async t => { - const {stdout} = await $`${parseCommandString('echo.js foo bar')}`; - t.is(stdout, 'foo\nbar'); -}); - -test('parseCommandString() + $.sync', t => { - const {stdout} = $.sync`${parseCommandString('echo.js foo bar')}`; - t.is(stdout, 'foo\nbar'); -}); - -test('parseCommandString() + execaNode', async t => { - const {stdout} = await execaNode(ECHO_FIXTURE_URL, parseCommandString('foo bar')); - t.is(stdout, 'foo\nbar'); -}); - -test('execaCommand(options)`...`', async t => { - const {stdout} = await execaCommand({stripFinalNewline: false})`${'echo.js foo bar'}`; - t.is(stdout, 'foo\nbar\n'); -}); - -test('execaCommandSync(options)`...`', t => { - const {stdout} = execaCommandSync({stripFinalNewline: false})`${'echo.js foo bar'}`; - t.is(stdout, 'foo\nbar\n'); -}); - -test('execaCommand(options)()', async t => { - const {stdout} = await execaCommand({stripFinalNewline: false})('echo.js foo bar'); - t.is(stdout, 'foo\nbar\n'); -}); - -test('execaCommandSync(options)()', t => { - const {stdout} = execaCommandSync({stripFinalNewline: false})('echo.js foo bar'); - t.is(stdout, 'foo\nbar\n'); -}); - -test('execaCommand().pipe(execaCommand())', async t => { - const {stdout} = await execaCommand('echo.js foo bar').pipe(execaCommand(`node ${STDIN_FIXTURE}`)); - t.is(stdout, 'foo\nbar'); -}); - -test('execaCommand().pipe(...) does not use execaCommand', async t => { - const {escapedCommand} = await execaCommand('echo.js foo bar').pipe(`node ${STDIN_FIXTURE}`, {reject: false}); - t.true(escapedCommand.startsWith(`${QUOTE}node `)); -}); - -test('execaCommand() bound options have lower priority', async t => { - const {stdout} = await execaCommand({stripFinalNewline: false})('echo.js foo bar', {stripFinalNewline: true}); - t.is(stdout, 'foo\nbar'); -}); - -test('execaCommandSync() bound options have lower priority', t => { - const {stdout} = execaCommandSync({stripFinalNewline: false})('echo.js foo bar', {stripFinalNewline: true}); - t.is(stdout, 'foo\nbar'); -}); - -const testInvalidArgumentsArray = (t, execaMethod) => { - t.throws(() => execaMethod('echo', ['foo']), { - message: /The command and its arguments must be passed as a single string/, - }); -}; - -test('execaCommand() must not pass an array of arguments', testInvalidArgumentsArray, execaCommand); -test('execaCommandSync() must not pass an array of arguments', testInvalidArgumentsArray, execaCommandSync); - -const testInvalidArgumentsTemplate = (t, execaMethod) => { - t.throws(() => execaMethod`echo foo`, { - message: /The command and its arguments must be passed as a single string/, - }); -}; - -test('execaCommand() must not pass an array of arguments with a template string', testInvalidArgumentsTemplate, execaCommand); -test('execaCommandSync() must not pass an array of arguments with a template string', testInvalidArgumentsTemplate, execaCommandSync); - -const testInvalidArgumentsParse = (t, command) => { - t.throws(() => parseCommandString(command), { - message: /The command must be a string/, - }); -}; - -test('execaCommand() must not pass a number', testInvalidArgumentsParse, 0); -test('execaCommand() must not pass undefined', testInvalidArgumentsParse, undefined); -test('execaCommand() must not pass null', testInvalidArgumentsParse, null); -test('execaCommand() must not pass a symbol', testInvalidArgumentsParse, Symbol('test')); -test('execaCommand() must not pass an object', testInvalidArgumentsParse, {}); -test('execaCommand() must not pass an array', testInvalidArgumentsParse, []); - -const testExecaCommandOutput = async (t, command, expectedOutput, execaMethod) => { - const {stdout} = await execaMethod(command); - t.is(stdout, expectedOutput); -}; - -test('execaCommand() allows escaping spaces in commands', testExecaCommandOutput, 'command\\ with\\ space.js foo bar', 'foo\nbar', execaCommand); -test('execaCommand() trims', testExecaCommandOutput, ' echo.js foo bar ', 'foo\nbar', execaCommand); -test('execaCommand() ignores consecutive spaces', testExecaCommandOutput, 'echo.js foo bar', 'foo\nbar', execaCommand); -test('execaCommand() escapes other whitespaces', testExecaCommandOutput, 'echo.js foo\tbar', 'foo\tbar', execaCommand); -test('execaCommand() allows escaping spaces', testExecaCommandOutput, 'echo.js foo\\ bar', 'foo bar', execaCommand); -test('execaCommand() allows escaping backslashes before spaces', testExecaCommandOutput, 'echo.js foo\\\\ bar', 'foo\\ bar', execaCommand); -test('execaCommand() allows escaping multiple backslashes before spaces', testExecaCommandOutput, 'echo.js foo\\\\\\\\ bar', 'foo\\\\\\ bar', execaCommand); -test('execaCommand() allows escaping backslashes not before spaces', testExecaCommandOutput, 'echo.js foo\\bar baz', 'foo\\bar\nbaz', execaCommand); -test('parseCommandString() allows escaping spaces in commands', testExecaCommandOutput, 'command\\ with\\ space.js foo bar', 'foo\nbar', parseAndRunCommand); -test('parseCommandString() trims', testExecaCommandOutput, ' echo.js foo bar ', 'foo\nbar', parseAndRunCommand); -test('parseCommandString() ignores consecutive spaces', testExecaCommandOutput, 'echo.js foo bar', 'foo\nbar', parseAndRunCommand); -test('parseCommandString() escapes other whitespaces', testExecaCommandOutput, 'echo.js foo\tbar', 'foo\tbar', parseAndRunCommand); -test('parseCommandString() allows escaping spaces', testExecaCommandOutput, 'echo.js foo\\ bar', 'foo bar', parseAndRunCommand); -test('parseCommandString() allows escaping backslashes before spaces', testExecaCommandOutput, 'echo.js foo\\\\ bar', 'foo\\ bar', parseAndRunCommand); -test('parseCommandString() allows escaping multiple backslashes before spaces', testExecaCommandOutput, 'echo.js foo\\\\\\\\ bar', 'foo\\\\\\ bar', parseAndRunCommand); -test('parseCommandString() allows escaping backslashes not before spaces', testExecaCommandOutput, 'echo.js foo\\bar baz', 'foo\\bar\nbaz', parseAndRunCommand); - -test('parseCommandString() can get empty strings', t => { - t.deepEqual(parseCommandString(''), []); -}); - -test('parseCommandString() can get only whitespaces', t => { - t.deepEqual(parseCommandString(' '), []); -}); diff --git a/test/methods/create.js b/test/methods/create.js deleted file mode 100644 index ad8b8c5537..0000000000 --- a/test/methods/create.js +++ /dev/null @@ -1,63 +0,0 @@ -import path from 'node:path'; -import test from 'ava'; -import { - execa, - execaSync, - execaNode, - $, -} from '../../index.js'; -import {foobarString, foobarArray} from '../helpers/input.js'; -import {setFixtureDirectory, FIXTURES_DIRECTORY} from '../helpers/fixtures-directory.js'; - -setFixtureDirectory(); - -const NOOP_PATH = path.join(FIXTURES_DIRECTORY, 'noop.js'); - -const testTemplate = async (t, execaMethod) => { - const {stdout} = await execaMethod`${NOOP_PATH} ${foobarString}`; - t.is(stdout, foobarString); -}; - -test('execa() can use template strings', testTemplate, execa); -test('execaNode() can use template strings', testTemplate, execaNode); -test('$ can use template strings', testTemplate, $); - -const testTemplateSync = (t, execaMethod) => { - const {stdout} = execaMethod`${NOOP_PATH} ${foobarString}`; - t.is(stdout, foobarString); -}; - -test('execaSync() can use template strings', testTemplateSync, execaSync); -test('$.sync can use template strings', testTemplateSync, $.sync); - -const testTemplateOptions = async (t, execaMethod) => { - const {stdout} = await execaMethod({stripFinalNewline: false})`${NOOP_PATH} ${foobarString}`; - t.is(stdout, `${foobarString}\n`); -}; - -test('execa() can use template strings with options', testTemplateOptions, execa); -test('execaNode() can use template strings with options', testTemplateOptions, execaNode); -test('$ can use template strings with options', testTemplateOptions, $); - -const testTemplateOptionsSync = (t, execaMethod) => { - const {stdout} = execaMethod({stripFinalNewline: false})`${NOOP_PATH} ${foobarString}`; - t.is(stdout, `${foobarString}\n`); -}; - -test('execaSync() can use template strings with options', testTemplateOptionsSync, execaSync); -test('$.sync can use template strings with options', testTemplateOptionsSync, $.sync); - -const testSpacedCommand = async (t, commandArguments, execaMethod) => { - const {stdout} = await execaMethod('command with space.js', commandArguments); - const expectedStdout = commandArguments === undefined ? '' : commandArguments.join('\n'); - t.is(stdout, expectedStdout); -}; - -test('allow commands with spaces and no array arguments', testSpacedCommand, undefined, execa); -test('allow commands with spaces and array arguments', testSpacedCommand, foobarArray, execa); -test('allow commands with spaces and no array arguments, execaSync', testSpacedCommand, undefined, execaSync); -test('allow commands with spaces and array arguments, execaSync', testSpacedCommand, foobarArray, execaSync); -test('allow commands with spaces and no array arguments, $', testSpacedCommand, undefined, $); -test('allow commands with spaces and array arguments, $', testSpacedCommand, foobarArray, $); -test('allow commands with spaces and no array arguments, $.sync', testSpacedCommand, undefined, $.sync); -test('allow commands with spaces and array arguments, $.sync', testSpacedCommand, foobarArray, $.sync); diff --git a/test/methods/main-async.js b/test/methods/main-async.js deleted file mode 100644 index 831ed68994..0000000000 --- a/test/methods/main-async.js +++ /dev/null @@ -1,26 +0,0 @@ -import process from 'node:process'; -import test from 'ava'; -import {execa} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; - -setFixtureDirectory(); - -const isWindows = process.platform === 'win32'; - -if (isWindows) { - test('execa() - cmd file', async t => { - const {stdout} = await execa('hello.cmd'); - t.is(stdout, 'Hello World'); - }); - - test('execa() - run cmd command', async t => { - const {stdout} = await execa('cmd', ['/c', 'hello.cmd']); - t.is(stdout, 'Hello World'); - }); -} - -test('execa() returns a promise with pid', async t => { - const subprocess = execa('noop.js', ['foo']); - t.is(typeof subprocess.pid, 'number'); - await subprocess; -}); diff --git a/test/methods/node.js b/test/methods/node.js deleted file mode 100644 index 05eff2ec06..0000000000 --- a/test/methods/node.js +++ /dev/null @@ -1,288 +0,0 @@ -import path from 'node:path'; -import process, {version} from 'node:process'; -import {pathToFileURL} from 'node:url'; -import test from 'ava'; -import getNode from 'get-node'; -import {execa, execaSync, execaNode} from '../../index.js'; -import {FIXTURES_DIRECTORY} from '../helpers/fixtures-directory.js'; -import {identity, fullStdio} from '../helpers/stdio.js'; -import {foobarString} from '../helpers/input.js'; - -process.chdir(FIXTURES_DIRECTORY); - -const runWithNodeOption = (file, commandArguments, options) => Array.isArray(commandArguments) - ? execa(file, commandArguments, {...options, node: true}) - : execa(file, {...options, node: true}); -const runWithNodeOptionSync = (file, commandArguments, options) => Array.isArray(commandArguments) - ? execaSync(file, commandArguments, {...options, node: true}) - : execaSync(file, {...options, node: true}); -const runWithIpc = (file, options) => execa('node', [file], {...options, ipc: true}); - -const testNodeSuccess = async (t, execaMethod) => { - const {exitCode, stdout} = await execaMethod('noop.js', [foobarString]); - t.is(exitCode, 0); - t.is(stdout, foobarString); -}; - -test('execaNode() succeeds', testNodeSuccess, execaNode); -test('The "node" option succeeds', testNodeSuccess, runWithNodeOption); -test('The "node" option succeeds - sync', testNodeSuccess, runWithNodeOptionSync); - -test('execaNode(options) succeeds', async t => { - const {stdout} = await execaNode({stripFinalNewline: false})('noop.js', [foobarString]); - t.is(stdout, `${foobarString}\n`); -}); - -test('execaNode`...` succeeds', async t => { - const {stdout} = await execaNode`noop.js ${foobarString}`; - t.is(stdout, foobarString); -}); - -test('execaNode().pipe(execaNode()) succeeds', async t => { - const {stdout} = await execaNode('noop.js').pipe(execaNode('--version')); - t.is(stdout, version); -}); - -test('execaNode().pipe(execa()) requires using "node"', async t => { - await t.throwsAsync(execaNode('noop.js').pipe(execa('--version'))); -}); - -test('execaNode().pipe(...) requires using "node"', async t => { - await t.throwsAsync(execaNode('noop.js').pipe('--version')); -}); - -test('execaNode().pipe`...` requires using "node"', async t => { - await t.throwsAsync(execaNode('noop.js').pipe`--version`); -}); - -test('execaNode() cannot set the "node" option to false', t => { - t.throws(() => { - execaNode('empty.js', {node: false}); - }, {message: /The "node" option cannot be false/}); -}); - -const testDoubleNode = (t, nodePath, execaMethod) => { - t.throws(() => { - execaMethod(nodePath, ['noop.js']); - }, {message: /does not need to be "node"/}); -}; - -test('Cannot use "node" as binary - execaNode()', testDoubleNode, 'node', execaNode); -test('Cannot use "node" as binary - "node" option', testDoubleNode, 'node', runWithNodeOption); -test('Cannot use "node" as binary - "node" option sync', testDoubleNode, 'node', runWithNodeOptionSync); -test('Cannot use path to "node" as binary - execaNode()', testDoubleNode, process.execPath, execaNode); -test('Cannot use path to "node" as binary - "node" option', testDoubleNode, process.execPath, runWithNodeOption); -test('Cannot use path to "node" as binary - "node" option sync', testDoubleNode, process.execPath, runWithNodeOptionSync); - -const getNodePath = async () => { - const {path} = await getNode(TEST_NODE_VERSION); - return path; -}; - -const TEST_NODE_VERSION = '16.0.0'; - -const testNodePath = async (t, execaMethod, mapPath) => { - const nodePath = mapPath(await getNodePath()); - const {stdout} = await execaMethod('--version', [], {nodePath}); - t.is(stdout, `v${TEST_NODE_VERSION}`); -}; - -test.serial('The "nodePath" option can be used - execaNode()', testNodePath, execaNode, identity); -test.serial('The "nodePath" option can be a file URL - execaNode()', testNodePath, execaNode, pathToFileURL); -test.serial('The "nodePath" option can be used - "node" option', testNodePath, runWithNodeOption, identity); -test.serial('The "nodePath" option can be a file URL - "node" option', testNodePath, runWithNodeOption, pathToFileURL); -test.serial('The "nodePath" option can be used - "node" option sync', testNodePath, runWithNodeOptionSync, identity); -test.serial('The "nodePath" option can be a file URL - "node" option sync', testNodePath, runWithNodeOptionSync, pathToFileURL); - -const testNodePathDefault = async (t, execaMethod) => { - const {stdout} = await execaMethod('--version'); - t.is(stdout, process.version); -}; - -test('The "nodePath" option defaults to the current Node.js binary - execaNode()', testNodePathDefault, execaNode); -test('The "nodePath" option defaults to the current Node.js binary - "node" option', testNodePathDefault, runWithNodeOption); -test('The "nodePath" option defaults to the current Node.js binary - "node" option sync', testNodePathDefault, runWithNodeOptionSync); - -const testNodePathInvalid = (t, execaMethod) => { - t.throws(() => { - execaMethod('noop.js', [], {nodePath: true}); - }, {message: /The "nodePath" option must be a string or a file URL/}); -}; - -test('The "nodePath" option must be a string or URL - execaNode()', testNodePathInvalid, execaNode); -test('The "nodePath" option must be a string or URL - "node" option', testNodePathInvalid, runWithNodeOption); -test('The "nodePath" option must be a string or URL - "node" option sync', testNodePathInvalid, runWithNodeOptionSync); - -const testFormerNodePath = (t, execaMethod) => { - t.throws(() => { - execaMethod('noop.js', [], {execPath: process.execPath}); - }, {message: /The "execPath" option has been removed/}); -}; - -test('The "execPath" option cannot be used - execaNode()', testFormerNodePath, execaNode); -test('The "execPath" option cannot be used - "node" option', testFormerNodePath, runWithNodeOption); -test('The "execPath" option cannot be used - "node" option sync', testFormerNodePath, runWithNodeOptionSync); - -const nodePathArguments = ['-p', ['process.env.Path || process.env.PATH']]; - -const testSubprocessNodePath = async (t, execaMethod, mapPath) => { - const nodePath = mapPath(await getNodePath()); - const {stdout} = await execaMethod(...nodePathArguments, {nodePath}); - t.true(stdout.includes(TEST_NODE_VERSION)); -}; - -test.serial('The "nodePath" option impacts the subprocess - execaNode()', testSubprocessNodePath, execaNode, identity); -test.serial('The "nodePath" option impacts the subprocess - "node" option', testSubprocessNodePath, runWithNodeOption, identity); -test.serial('The "nodePath" option impacts the subprocess - "node" option sync', testSubprocessNodePath, runWithNodeOptionSync, identity); - -const testSubprocessNodePathDefault = async (t, execaMethod) => { - const {stdout} = await execaMethod(...nodePathArguments); - t.true(stdout.includes(path.dirname(process.execPath))); -}; - -test('The "nodePath" option defaults to the current Node.js binary in the subprocess - execaNode()', testSubprocessNodePathDefault, execaNode); -test('The "nodePath" option defaults to the current Node.js binary in the subprocess - "node" option', testSubprocessNodePathDefault, runWithNodeOption); -test('The "nodePath" option defaults to the current Node.js binary in the subprocess - "node" option sync', testSubprocessNodePathDefault, runWithNodeOptionSync); - -test.serial('The "nodePath" option requires "node: true" to impact the subprocess', async t => { - const nodePath = await getNodePath(); - const {stdout} = await execa('node', nodePathArguments.flat(), {nodePath}); - t.false(stdout.includes(TEST_NODE_VERSION)); -}); - -const testSubprocessNodePathCwd = async (t, execaMethod) => { - const nodePath = await getNodePath(); - const cwd = path.dirname(path.dirname(nodePath)); - const relativeExecPath = path.relative(cwd, nodePath); - const {stdout} = await execaMethod(...nodePathArguments, {nodePath: relativeExecPath, cwd}); - t.true(stdout.includes(TEST_NODE_VERSION)); -}; - -test.serial('The "nodePath" option is relative to "cwd" when used in the subprocess - execaNode()', testSubprocessNodePathCwd, execaNode); -test.serial('The "nodePath" option is relative to "cwd" when used in the subprocess - "node" option', testSubprocessNodePathCwd, runWithNodeOption); -test.serial('The "nodePath" option is relative to "cwd" when used in the subprocess - "node" option sync', testSubprocessNodePathCwd, runWithNodeOptionSync); - -const testCwdNodePath = async (t, execaMethod) => { - const nodePath = await getNodePath(); - const cwd = path.dirname(path.dirname(nodePath)); - const relativeExecPath = path.relative(cwd, nodePath); - const {stdout} = await execaMethod('--version', [], {nodePath: relativeExecPath, cwd}); - t.is(stdout, `v${TEST_NODE_VERSION}`); -}; - -test.serial('The "nodePath" option is relative to "cwd" - execaNode()', testCwdNodePath, execaNode); -test.serial('The "nodePath" option is relative to "cwd" - "node" option', testCwdNodePath, runWithNodeOption); -test.serial('The "nodePath" option is relative to "cwd" - "node" option sync', testCwdNodePath, runWithNodeOptionSync); - -const testNodeOptions = async (t, execaMethod) => { - const {stdout} = await execaMethod('empty.js', [], {nodeOptions: ['--version']}); - t.is(stdout, process.version); -}; - -test('The "nodeOptions" option can be used - execaNode()', testNodeOptions, execaNode); -test('The "nodeOptions" option can be used - "node" option', testNodeOptions, runWithNodeOption); -test('The "nodeOptions" option can be used - "node" option sync', testNodeOptions, runWithNodeOptionSync); - -const spawnNestedExecaNode = (realExecArgv, fakeExecArgv, execaMethod, nodeOptions) => execa( - 'node', - [...realExecArgv, 'nested-node.js', fakeExecArgv, execaMethod, nodeOptions, 'noop.js', foobarString], - {...fullStdio, cwd: FIXTURES_DIRECTORY}, -); - -const testInspectRemoval = async (t, fakeExecArgv, execaMethod) => { - const {stdout, stdio} = await spawnNestedExecaNode([], fakeExecArgv, execaMethod, ''); - t.is(stdout, foobarString); - t.is(stdio[3], ''); -}; - -test('The "nodeOptions" option removes --inspect without a port when defined by current process - execaNode()', testInspectRemoval, '--inspect', 'execaNode'); -test('The "nodeOptions" option removes --inspect without a port when defined by current process - "node" option', testInspectRemoval, '--inspect', 'nodeOption'); -test('The "nodeOptions" option removes --inspect with a port when defined by current process - execaNode()', testInspectRemoval, '--inspect=9222', 'execaNode'); -test('The "nodeOptions" option removes --inspect with a port when defined by current process - "node" option', testInspectRemoval, '--inspect=9222', 'nodeOption'); -test('The "nodeOptions" option removes --inspect-brk without a port when defined by current process - execaNode()', testInspectRemoval, '--inspect-brk', 'execaNode'); -test('The "nodeOptions" option removes --inspect-brk without a port when defined by current process - "node" option', testInspectRemoval, '--inspect-brk', 'nodeOption'); -test('The "nodeOptions" option removes --inspect-brk with a port when defined by current process - execaNode()', testInspectRemoval, '--inspect-brk=9223', 'execaNode'); -test('The "nodeOptions" option removes --inspect-brk with a port when defined by current process - "node" option', testInspectRemoval, '--inspect-brk=9223', 'nodeOption'); - -const testInspectDifferentPort = async (t, execaMethod) => { - const {stdout, stdio} = await spawnNestedExecaNode(['--inspect=9225'], '', execaMethod, '--inspect=9224'); - t.is(stdout, foobarString); - t.true(stdio[3].includes('Debugger listening')); -}; - -test.serial('The "nodeOptions" option allows --inspect with a different port even when defined by current process - execaNode()', testInspectDifferentPort, 'execaNode'); -test.serial('The "nodeOptions" option allows --inspect with a different port even when defined by current process - "node" option', testInspectDifferentPort, 'nodeOption'); - -const testInspectSamePort = async (t, execaMethod) => { - const {stdout, stdio} = await spawnNestedExecaNode(['--inspect=9226'], '', execaMethod, '--inspect=9226'); - t.is(stdout, foobarString); - t.true(stdio[3].includes('address already in use')); -}; - -test.serial('The "nodeOptions" option forbids --inspect with the same port when defined by current process - execaNode()', testInspectSamePort, 'execaNode'); -test.serial('The "nodeOptions" option forbids --inspect with the same port when defined by current process - "node" option', testInspectSamePort, 'nodeOption'); - -const testIpc = async (t, execaMethod, options) => { - const subprocess = execaMethod('ipc-echo.js', [], options); - - await subprocess.sendMessage(foobarString); - t.is(await subprocess.getOneMessage(), foobarString); - - const {stdio} = await subprocess; - t.is(stdio.length, 4); - t.is(stdio[3], undefined); -}; - -test('execaNode() adds an ipc channel', testIpc, execaNode, {}); -test('The "node" option adds an ipc channel', testIpc, runWithNodeOption, {}); -test('The "ipc" option adds an ipc channel', testIpc, runWithIpc, {}); -test('The "ipc" option works with "stdio: \'pipe\'"', testIpc, runWithIpc, {stdio: 'pipe'}); -test('The "ipc" option works with "stdio: [\'pipe\', \'pipe\', \'pipe\']"', testIpc, runWithIpc, {stdio: ['pipe', 'pipe', 'pipe']}); -test('The "ipc" option works with "stdio: [\'pipe\', \'pipe\', \'pipe\', \'ipc\']"', testIpc, runWithIpc, {stdio: ['pipe', 'pipe', 'pipe', 'ipc']}); -test('The "ipc" option works with "stdout: \'pipe\'"', testIpc, runWithIpc, {stdout: 'pipe'}); - -const NO_SEND_MESSAGE = 'sendMessage() can only be used'; - -test('No ipc channel is added by default', async t => { - const {message, stdio} = await t.throwsAsync(execa('node', ['ipc-send.js'])); - t.true(message.includes(NO_SEND_MESSAGE)); - t.is(stdio.length, 3); -}); - -const testDisableIpc = async (t, execaMethod) => { - const {failed, message, stdio} = await execaMethod('ipc-send.js', [], {ipc: false, reject: false}); - t.true(failed); - t.true(message.includes(NO_SEND_MESSAGE)); - t.is(stdio.length, 3); -}; - -test('Can disable "ipc" - execaNode()', testDisableIpc, execaNode); -test('Can disable "ipc" - "node" option', testDisableIpc, runWithNodeOption); -test('Can disable "ipc" - "node" option sync', testDisableIpc, runWithNodeOptionSync); - -const NO_IPC_MESSAGE = /The "ipc: true" option cannot be used/; - -const testNoIpcSync = (t, node) => { - t.throws(() => { - execaSync('node', ['ipc-send.js'], {ipc: true, node}); - }, {message: NO_IPC_MESSAGE}); -}; - -test('Cannot use "ipc: true" with execaSync()', testNoIpcSync, undefined); -test('Cannot use "ipc: true" with execaSync() - "node: false"', testNoIpcSync, false); - -test('Cannot use "ipc: true" with execaSync() - "node: true"', t => { - t.throws(() => { - execaSync('ipc-send.js', {ipc: true, node: true}); - }, {message: NO_IPC_MESSAGE}); -}); - -const testNoShell = async (t, execaMethod) => { - const {failed, message} = await execaMethod('node --version', [], {shell: true, reject: false}); - t.true(failed); - t.true(message.includes('MODULE_NOT_FOUND')); -}; - -test('Cannot use "shell: true" - execaNode()', testNoShell, execaNode); -test('Cannot use "shell: true" - "node" option', testNoShell, runWithNodeOption); -test('Cannot use "shell: true" - "node" option sync', testNoShell, runWithNodeOptionSync); diff --git a/test/methods/override-promise.js b/test/methods/override-promise.js deleted file mode 100644 index d5db5dba42..0000000000 --- a/test/methods/override-promise.js +++ /dev/null @@ -1,13 +0,0 @@ -import test from 'ava'; -// The helper module overrides Promise on import so has to be imported before `execa`. -import {restorePromise} from '../helpers/override-promise.js'; -import {execa} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; - -restorePromise(); -setFixtureDirectory(); - -test('should work with third-party Promise', async t => { - const {stdout} = await execa('noop.js', ['foo']); - t.is(stdout, 'foo'); -}); diff --git a/test/methods/parameters-args.js b/test/methods/parameters-args.js deleted file mode 100644 index 9a1c8b7a11..0000000000 --- a/test/methods/parameters-args.js +++ /dev/null @@ -1,54 +0,0 @@ -import test from 'ava'; -import { - execa, - execaSync, - execaCommand, - execaCommandSync, - execaNode, - $, -} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; - -setFixtureDirectory(); - -const testInvalidArguments = async (t, execaMethod) => { - t.throws(() => { - execaMethod('echo', true); - }, {message: /Second argument must be either/}); -}; - -test('execa()\'s second argument must be valid', testInvalidArguments, execa); -test('execaSync()\'s second argument must be valid', testInvalidArguments, execaSync); -test('execaCommand()\'s second argument must be valid', testInvalidArguments, execaCommand); -test('execaCommandSync()\'s second argument must be valid', testInvalidArguments, execaCommandSync); -test('execaNode()\'s second argument must be valid', testInvalidArguments, execaNode); -test('$\'s second argument must be valid', testInvalidArguments, $); -test('$.sync\'s second argument must be valid', testInvalidArguments, $.sync); - -const testInvalidArgumentsItems = async (t, execaMethod) => { - t.throws(() => { - execaMethod('echo', [{}]); - }, {message: 'Second argument must be an array of strings: [object Object]'}); -}; - -test('execa()\'s second argument must not be objects', testInvalidArgumentsItems, execa); -test('execaSync()\'s second argument must not be objects', testInvalidArgumentsItems, execaSync); -test('execaCommand()\'s second argument must not be objects', testInvalidArgumentsItems, execaCommand); -test('execaCommandSync()\'s second argument must not be objects', testInvalidArgumentsItems, execaCommandSync); -test('execaNode()\'s second argument must not be objects', testInvalidArgumentsItems, execaNode); -test('$\'s second argument must not be objects', testInvalidArgumentsItems, $); -test('$.sync\'s second argument must not be objects', testInvalidArgumentsItems, $.sync); - -const testNullByteArgument = async (t, execaMethod) => { - t.throws(() => { - execaMethod('echo', ['a\0b']); - }, {message: /null bytes/}); -}; - -test('execa()\'s second argument must not include \\0', testNullByteArgument, execa); -test('execaSync()\'s second argument must not include \\0', testNullByteArgument, execaSync); -test('execaCommand()\'s second argument must not include \\0', testNullByteArgument, execaCommand); -test('execaCommandSync()\'s second argument must not include \\0', testNullByteArgument, execaCommandSync); -test('execaNode()\'s second argument must not include \\0', testNullByteArgument, execaNode); -test('$\'s second argument must not include \\0', testNullByteArgument, $); -test('$.sync\'s second argument must not include \\0', testNullByteArgument, $.sync); diff --git a/test/methods/parameters-command.js b/test/methods/parameters-command.js deleted file mode 100644 index e24c1afb18..0000000000 --- a/test/methods/parameters-command.js +++ /dev/null @@ -1,88 +0,0 @@ -import path from 'node:path'; -import {fileURLToPath} from 'node:url'; -import test from 'ava'; -import { - execa, - execaSync, - execaCommand, - execaCommandSync, - execaNode, - $, -} from '../../index.js'; -import {setFixtureDirectory, FIXTURES_DIRECTORY_URL} from '../helpers/fixtures-directory.js'; -import {foobarString} from '../helpers/input.js'; - -setFixtureDirectory(); - -const testFileUrl = async (t, execaMethod) => { - const command = new URL('noop.js', FIXTURES_DIRECTORY_URL); - const {stdout} = await execaMethod(command); - t.is(stdout, foobarString); -}; - -test('execa()\'s command argument can be a file URL', testFileUrl, execa); -test('execaSync()\'s command argument can be a file URL', testFileUrl, execaSync); -test('execaCommand()\'s command argument can be a file URL', testFileUrl, execaCommand); -test('execaCommandSync()\'s command argument can be a file URL', testFileUrl, execaCommandSync); -test('execaNode()\'s command argument can be a file URL', testFileUrl, execaNode); -test('$\'s command argument can be a file URL', testFileUrl, $); -test('$.sync\'s command argument can be a file URL', testFileUrl, $.sync); - -const testInvalidFileUrl = async (t, execaMethod) => { - const invalidUrl = new URL('https://invalid.com'); - t.throws(() => { - execaMethod(invalidUrl); - }, {code: 'ERR_INVALID_URL_SCHEME'}); -}; - -test('execa()\'s command argument cannot be a non-file URL', testInvalidFileUrl, execa); -test('execaSync()\'s command argument cannot be a non-file URL', testInvalidFileUrl, execaSync); -test('execaCommand()\'s command argument cannot be a non-file URL', testInvalidFileUrl, execaCommand); -test('execaCommandSync()\'s command argument cannot be a non-file URL', testInvalidFileUrl, execaCommandSync); -test('execaNode()\'s command argument cannot be a non-file URL', testInvalidFileUrl, execaNode); -test('$\'s command argument cannot be a non-file URL', testInvalidFileUrl, $); -test('$.sync\'s command argument cannot be a non-file URL', testInvalidFileUrl, $.sync); - -const testInvalidCommand = async (t, commandArgument, execaMethod) => { - t.throws(() => { - execaMethod(commandArgument); - }, {message: /First argument must be a string or a file URL/}); -}; - -test('execa()\'s first argument must be defined', testInvalidCommand, undefined, execa); -test('execaSync()\'s first argument must be defined', testInvalidCommand, undefined, execaSync); -test('execaCommand()\'s first argument must be defined', testInvalidCommand, undefined, execaCommand); -test('execaCommandSync()\'s first argument must be defined', testInvalidCommand, undefined, execaCommandSync); -test('execaNode()\'s first argument must be defined', testInvalidCommand, undefined, execaNode); -test('$\'s first argument must be defined', testInvalidCommand, undefined, $); -test('$.sync\'s first argument must be defined', testInvalidCommand, undefined, $.sync); -test('execa()\'s first argument must be valid', testInvalidCommand, true, execa); -test('execaSync()\'s first argument must be valid', testInvalidCommand, true, execaSync); -test('execaCommand()\'s first argument must be valid', testInvalidCommand, true, execaCommand); -test('execaCommandSync()\'s first argument must be valid', testInvalidCommand, true, execaCommandSync); -test('execaNode()\'s first argument must be valid', testInvalidCommand, true, execaNode); -test('$\'s first argument must be valid', testInvalidCommand, true, $); -test('$.sync\'s first argument must be valid', testInvalidCommand, true, $.sync); -test('execa()\'s command argument must be a string or file URL', testInvalidCommand, ['command', 'arg'], execa); -test('execaSync()\'s command argument must be a string or file URL', testInvalidCommand, ['command', 'arg'], execaSync); -test('execaCommand()\'s command argument must be a string or file URL', testInvalidCommand, ['command', 'arg'], execaCommand); -test('execaCommandSync()\'s command argument must be a string or file URL', testInvalidCommand, ['command', 'arg'], execaCommandSync); -test('execaNode()\'s command argument must be a string or file URL', testInvalidCommand, ['command', 'arg'], execaNode); -test('$\'s command argument must be a string or file URL', testInvalidCommand, ['command', 'arg'], $); -test('$.sync\'s command argument must be a string or file URL', testInvalidCommand, ['command', 'arg'], $.sync); - -const testRelativePath = async (t, execaMethod) => { - // @todo: use import.meta.dirname after dropping support for Node <20.11.0 - const rootDirectory = path.basename(fileURLToPath(new URL('../..', import.meta.url))); - const pathViaParentDirectory = path.join('..', rootDirectory, 'test', 'fixtures', 'noop.js'); - const {stdout} = await execaMethod(pathViaParentDirectory); - t.is(stdout, foobarString); -}; - -test('execa() use relative path with \'..\' chars', testRelativePath, execa); -test('execaSync() use relative path with \'..\' chars', testRelativePath, execaSync); -test('execaCommand() use relative path with \'..\' chars', testRelativePath, execaCommand); -test('execaCommandSync() use relative path with \'..\' chars', testRelativePath, execaCommandSync); -test('execaNode() use relative path with \'..\' chars', testRelativePath, execaNode); -test('$ use relative path with \'..\' chars', testRelativePath, $); -test('$.sync use relative path with \'..\' chars', testRelativePath, $.sync); diff --git a/test/methods/parameters-options.js b/test/methods/parameters-options.js deleted file mode 100644 index 2c7119d003..0000000000 --- a/test/methods/parameters-options.js +++ /dev/null @@ -1,75 +0,0 @@ -import path from 'node:path'; -import test from 'ava'; -import { - execa, - execaSync, - execaCommand, - execaCommandSync, - execaNode, - $, -} from '../../index.js'; -import {setFixtureDirectory, FIXTURES_DIRECTORY} from '../helpers/fixtures-directory.js'; - -setFixtureDirectory(); - -const NOOP_PATH = path.join(FIXTURES_DIRECTORY, 'noop.js'); - -const testSerializeArgument = async (t, commandArgument, execaMethod) => { - const {stdout} = await execaMethod(NOOP_PATH, [commandArgument]); - t.is(stdout, String(commandArgument)); -}; - -test('execa()\'s arguments can be numbers', testSerializeArgument, 1, execa); -test('execa()\'s arguments can be booleans', testSerializeArgument, true, execa); -test('execa()\'s arguments can be NaN', testSerializeArgument, Number.NaN, execa); -test('execa()\'s arguments can be Infinity', testSerializeArgument, Number.POSITIVE_INFINITY, execa); -test('execa()\'s arguments can be null', testSerializeArgument, null, execa); -test('execa()\'s arguments can be undefined', testSerializeArgument, undefined, execa); -test('execa()\'s arguments can be bigints', testSerializeArgument, 1n, execa); -test('execa()\'s arguments can be symbols', testSerializeArgument, Symbol('test'), execa); -test('execaSync()\'s arguments can be numbers', testSerializeArgument, 1, execaSync); -test('execaSync()\'s arguments can be booleans', testSerializeArgument, true, execaSync); -test('execaSync()\'s arguments can be NaN', testSerializeArgument, Number.NaN, execaSync); -test('execaSync()\'s arguments can be Infinity', testSerializeArgument, Number.POSITIVE_INFINITY, execaSync); -test('execaSync()\'s arguments can be null', testSerializeArgument, null, execaSync); -test('execaSync()\'s arguments can be undefined', testSerializeArgument, undefined, execaSync); -test('execaSync()\'s arguments can be bigints', testSerializeArgument, 1n, execaSync); -test('execaSync()\'s arguments can be symbols', testSerializeArgument, Symbol('test'), execaSync); -test('execaNode()\'s arguments can be numbers', testSerializeArgument, 1, execaNode); -test('execaNode()\'s arguments can be booleans', testSerializeArgument, true, execaNode); -test('execaNode()\'s arguments can be NaN', testSerializeArgument, Number.NaN, execaNode); -test('execaNode()\'s arguments can be Infinity', testSerializeArgument, Number.POSITIVE_INFINITY, execaNode); -test('execaNode()\'s arguments can be null', testSerializeArgument, null, execaNode); -test('execaNode()\'s arguments can be undefined', testSerializeArgument, undefined, execaNode); -test('execaNode()\'s arguments can be bigints', testSerializeArgument, 1n, execaNode); -test('execaNode()\'s arguments can be symbols', testSerializeArgument, Symbol('test'), execaNode); -test('$\'s arguments can be numbers', testSerializeArgument, 1, $); -test('$\'s arguments can be booleans', testSerializeArgument, true, $); -test('$\'s arguments can be NaN', testSerializeArgument, Number.NaN, $); -test('$\'s arguments can be Infinity', testSerializeArgument, Number.POSITIVE_INFINITY, $); -test('$\'s arguments can be null', testSerializeArgument, null, $); -test('$\'s arguments can be undefined', testSerializeArgument, undefined, $); -test('$\'s arguments can be bigints', testSerializeArgument, 1n, $); -test('$\'s arguments can be symbols', testSerializeArgument, Symbol('test'), $); -test('$.sync\'s arguments can be numbers', testSerializeArgument, 1, $.sync); -test('$.sync\'s arguments can be booleans', testSerializeArgument, true, $.sync); -test('$.sync\'s arguments can be NaN', testSerializeArgument, Number.NaN, $.sync); -test('$.sync\'s arguments can be Infinity', testSerializeArgument, Number.POSITIVE_INFINITY, $.sync); -test('$.sync\'s arguments can be null', testSerializeArgument, null, $.sync); -test('$.sync\'s arguments can be undefined', testSerializeArgument, undefined, $.sync); -test('$.sync\'s arguments can be bigints', testSerializeArgument, 1n, $.sync); -test('$.sync\'s arguments can be symbols', testSerializeArgument, Symbol('test'), $.sync); - -const testInvalidOptions = async (t, execaMethod) => { - t.throws(() => { - execaMethod('echo', [], new Map()); - }, {message: /Last argument must be an options object/}); -}; - -test('execa()\'s third argument must be a plain object', testInvalidOptions, execa); -test('execaSync()\'s third argument must be a plain object', testInvalidOptions, execaSync); -test('execaCommand()\'s third argument must be a plain object', testInvalidOptions, execaCommand); -test('execaCommandSync()\'s third argument must be a plain object', testInvalidOptions, execaCommandSync); -test('execaNode()\'s third argument must be a plain object', testInvalidOptions, execaNode); -test('$\'s third argument must be a plain object', testInvalidOptions, $); -test('$.sync\'s third argument must be a plain object', testInvalidOptions, $.sync); diff --git a/test/methods/promise.js b/test/methods/promise.js deleted file mode 100644 index ded4b2bea8..0000000000 --- a/test/methods/promise.js +++ /dev/null @@ -1,53 +0,0 @@ -import test from 'ava'; -import {execa} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; - -setFixtureDirectory(); - -test('promise methods are not enumerable', t => { - const descriptors = Object.getOwnPropertyDescriptors(execa('noop.js')); - t.false(descriptors.then.enumerable); - t.false(descriptors.catch.enumerable); - t.false(descriptors.finally.enumerable); -}); - -test('finally function is executed on success', async t => { - let isCalled = false; - const {stdout} = await execa('noop.js', ['foo']).finally(() => { - isCalled = true; - }); - t.is(isCalled, true); - t.is(stdout, 'foo'); -}); - -test('finally function is executed on failure', async t => { - let isError = false; - const {stdout, stderr} = await t.throwsAsync(execa('exit.js', ['2']).finally(() => { - isError = true; - })); - t.is(isError, true); - t.is(typeof stdout, 'string'); - t.is(typeof stderr, 'string'); -}); - -test('throw in finally function bubbles up on success', async t => { - const {message} = await t.throwsAsync(execa('noop.js', ['foo']).finally(() => { - throw new Error('called'); - })); - t.is(message, 'called'); -}); - -test('throw in finally bubbles up on error', async t => { - const {message} = await t.throwsAsync(execa('exit.js', ['2']).finally(() => { - throw new Error('called'); - })); - t.is(message, 'called'); -}); - -const testNoAwait = async (t, fixtureName, options, message) => { - const {stdout} = await execa('no-await.js', [JSON.stringify(options), fixtureName]); - t.true(stdout.includes(message)); -}; - -test('Throws if promise is not awaited and subprocess fails', testNoAwait, 'fail.js', {}, 'exit code 2'); -test('Throws if promise is not awaited and subprocess times out', testNoAwait, 'forever.js', {timeout: 1}, 'timed out'); diff --git a/test/methods/script.js b/test/methods/script.js deleted file mode 100644 index ac33b4b67b..0000000000 --- a/test/methods/script.js +++ /dev/null @@ -1,59 +0,0 @@ -import test from 'ava'; -import {isStream} from 'is-stream'; -import {$} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString} from '../helpers/input.js'; - -setFixtureDirectory(); - -const testScriptStdoutSync = (t, getSubprocess, expectedStdout) => { - const {stdout} = getSubprocess(); - t.is(stdout, expectedStdout); -}; - -test('$.sync`...`', testScriptStdoutSync, () => $.sync`echo.js foo bar`, 'foo\nbar'); -test('$.s`...`', testScriptStdoutSync, () => $.s`echo.js foo bar`, 'foo\nbar'); -test('$(options).sync`...`', testScriptStdoutSync, () => $({stripFinalNewline: false}).sync`echo.js ${foobarString}`, `${foobarString}\n`); -test('$.sync(options)`...`', testScriptStdoutSync, () => $.sync({stripFinalNewline: false})`echo.js ${foobarString}`, `${foobarString}\n`); - -test('Cannot call $.sync.sync', t => { - t.false('sync' in $.sync); -}); - -test('Cannot call $.sync(options).sync', t => { - t.false('sync' in $.sync({})); -}); - -test('$(options)() stdin defaults to "inherit"', async t => { - const {stdout} = await $({input: foobarString})('stdin-script.js'); - t.is(stdout, foobarString); -}); - -test('$.sync(options)() stdin defaults to "inherit"', t => { - const {stdout} = $.sync({input: foobarString})('stdin-script.js'); - t.is(stdout, foobarString); -}); - -test('$(options).sync() stdin defaults to "inherit"', t => { - const {stdout} = $({input: foobarString}).sync('stdin-script.js'); - t.is(stdout, foobarString); -}); - -test('$(options)`...` stdin defaults to "inherit"', async t => { - const {stdout} = await $({input: foobarString})`stdin-script.js`; - t.is(stdout, foobarString); -}); - -test('$.sync(options)`...` stdin defaults to "inherit"', t => { - const {stdout} = $.sync({input: foobarString})`stdin-script.js`; - t.is(stdout, foobarString); -}); - -test('$(options).sync`...` stdin defaults to "inherit"', t => { - const {stdout} = $({input: foobarString}).sync`stdin-script.js`; - t.is(stdout, foobarString); -}); - -test('$ stdin has no default value when stdio is set', t => { - t.true(isStream($({stdio: 'pipe'})`noop.js`.stdin)); -}); diff --git a/test/methods/template.js b/test/methods/template.js deleted file mode 100644 index c1efde9ca8..0000000000 --- a/test/methods/template.js +++ /dev/null @@ -1,344 +0,0 @@ -import test from 'ava'; -import {$} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; - -setFixtureDirectory(); - -// Workaround since some text editors or IDEs do not allow inputting \r directly -const escapedCall = string => { - const templates = [string]; - templates.raw = [string]; - return $(templates); -}; - -const testScriptStdout = async (t, getSubprocess, expectedStdout) => { - const {stdout} = await getSubprocess(); - t.is(stdout, expectedStdout); -}; - -test('$ allows number interpolation', testScriptStdout, () => $`echo.js 1 ${2}`, '1\n2'); -test('$ can concatenate multiple tokens', testScriptStdout, () => $`echo.js ${'foo'}bar${'foo'}`, 'foobarfoo'); -test('$ can use newlines and tab indentations', testScriptStdout, () => $`echo.js foo - bar`, 'foo\nbar'); -test('$ can use newlines and space indentations', testScriptStdout, () => $`echo.js foo - bar`, 'foo\nbar'); -test('$ can use Windows newlines and tab indentations', testScriptStdout, () => escapedCall('echo.js foo\r\n\tbar'), 'foo\nbar'); -test('$ can use Windows newlines and space indentations', testScriptStdout, () => escapedCall('echo.js foo\r\n bar'), 'foo\nbar'); -test('$ does not ignore comments in expressions', testScriptStdout, () => $`echo.js foo - ${/* This is a comment */''} - bar - ${/* This is another comment */''} - baz -`, 'foo\n\nbar\n\nbaz'); -test('$ allows escaping spaces with interpolation', testScriptStdout, () => $`echo.js ${'foo bar'}`, 'foo bar'); -test('$ allows escaping spaces in commands with interpolation', testScriptStdout, () => $`${'command with space.js'} foo bar`, 'foo\nbar'); -test('$ trims', testScriptStdout, () => $` echo.js foo bar `, 'foo\nbar'); -test('$ allows array interpolation', testScriptStdout, () => $`echo.js ${['foo', 'bar']}`, 'foo\nbar'); -test('$ allows empty array interpolation', testScriptStdout, () => $`echo.js foo ${[]} bar`, 'foo\nbar'); -test('$ allows space escaped values in array interpolation', testScriptStdout, () => $`echo.js ${['foo', 'bar baz']}`, 'foo\nbar baz'); -test('$ can concatenate at the end of tokens followed by an array', testScriptStdout, () => $`echo.js foo${['bar', 'foo']}`, 'foobar\nfoo'); -test('$ can concatenate at the start of tokens followed by an array', testScriptStdout, () => $`echo.js ${['foo', 'bar']}foo`, 'foo\nbarfoo'); -test('$ can concatenate at the start and end of tokens followed by an array', testScriptStdout, () => $`echo.js foo${['bar', 'foo']}bar`, 'foobar\nfoobar'); -test('$ handles escaped newlines', testScriptStdout, () => $`echo.js a\ -b`, 'ab'); -test('$ handles backslashes at end of lines', testScriptStdout, () => $`echo.js a\\ - b`, 'a\\\nb'); -test('$ handles double backslashes at end of lines', testScriptStdout, () => $`echo.js a\\\\ - b`, 'a\\\\\nb'); -test('$ handles tokens - a', testScriptStdout, () => $`echo.js a`, 'a'); -test('$ handles expressions - a', testScriptStdout, () => $`echo.js ${'a'}`, 'a'); -test('$ handles tokens - abc', testScriptStdout, () => $`echo.js abc`, 'abc'); -test('$ handles expressions - abc', testScriptStdout, () => $`echo.js ${'abc'}`, 'abc'); -test('$ handles tokens - ""', testScriptStdout, () => $`echo.js`, ''); -test('$ handles expressions - ""', testScriptStdout, () => $`echo.js a ${''} b`, 'a\n\nb'); -test('$ splits tokens - ""', testScriptStdout, () => $`echo.js ab`, 'ab'); -test('$ splits expressions - ""', testScriptStdout, () => $`echo.js ${'a'}${'b'}`, 'ab'); -test('$ concatenates expressions - ""', testScriptStdout, () => $`echo.js a${'b'}c`, 'abc'); -test('$ handles tokens - " "', testScriptStdout, () => $`echo.js `, ''); -test('$ handles expressions - " "', testScriptStdout, () => $`echo.js ${' '}`, ' '); -test('$ splits tokens - " "', testScriptStdout, () => $`echo.js a b`, 'a\nb'); -test('$ splits expressions - " "', testScriptStdout, () => $`echo.js ${'a'} ${'b'}`, 'a\nb'); -test('$ concatenates tokens - " "', testScriptStdout, () => $`echo.js a `, 'a'); -test('$ concatenates expressions - " "', testScriptStdout, () => $`echo.js ${'a'} `, 'a'); -test('$ handles tokens - " " (2 spaces)', testScriptStdout, () => $`echo.js `, ''); -test('$ handles expressions - " " (2 spaces)', testScriptStdout, () => $`echo.js ${' '}`, ' '); -test('$ splits tokens - " " (2 spaces)', testScriptStdout, () => $`echo.js a b`, 'a\nb'); -test('$ splits expressions - " " (2 spaces)', testScriptStdout, () => $`echo.js ${'a'} ${'b'}`, 'a\nb'); -test('$ concatenates tokens - " " (2 spaces)', testScriptStdout, () => $`echo.js a `, 'a'); -test('$ concatenates expressions - " " (2 spaces)', testScriptStdout, () => $`echo.js ${'a'} `, 'a'); -test('$ handles tokens - " " (3 spaces)', testScriptStdout, () => $`echo.js `, ''); -test('$ handles expressions - " " (3 spaces)', testScriptStdout, () => $`echo.js ${' '}`, ' '); -test('$ splits tokens - " " (3 spaces)', testScriptStdout, () => $`echo.js a b`, 'a\nb'); -test('$ splits expressions - " " (3 spaces)', testScriptStdout, () => $`echo.js ${'a'} ${'b'}`, 'a\nb'); -test('$ concatenates tokens - " " (3 spaces)', testScriptStdout, () => $`echo.js a `, 'a'); -test('$ concatenates expressions - " " (3 spaces)', testScriptStdout, () => $`echo.js ${'a'} `, 'a'); -test('$ handles tokens - \\t (no escape)', testScriptStdout, () => $`echo.js `, ''); -test('$ handles expressions - \\t (no escape)', testScriptStdout, () => $`echo.js ${' '}`, '\t'); -test('$ splits tokens - \\t (no escape)', testScriptStdout, () => $`echo.js a b`, 'a\nb'); -test('$ splits expressions - \\t (no escape)', testScriptStdout, () => $`echo.js ${'a'} ${'b'}`, 'a\nb'); -test('$ concatenates tokens - \\t (no escape)', testScriptStdout, () => $`echo.js a b`, 'a\nb'); -test('$ concatenates expressions - \\t (no escape)', testScriptStdout, () => $`echo.js ${'a'} b`, 'a\nb'); -test('$ handles tokens - \\t (escape)', testScriptStdout, () => $`echo.js \t`, '\t'); -test('$ handles expressions - \\t (escape)', testScriptStdout, () => $`echo.js ${'\t'}`, '\t'); -test('$ splits tokens - \\t (escape)', testScriptStdout, () => $`echo.js a\tb`, 'a\tb'); -test('$ splits expressions - \\t (escape)', testScriptStdout, () => $`echo.js ${'a'}\t${'b'}`, 'a\tb'); -test('$ concatenates tokens - \\t (escape)', testScriptStdout, () => $`echo.js \ta\t b`, '\ta\t\nb'); -test('$ concatenates expressions - \\t (escape)', testScriptStdout, () => $`echo.js \t${'a'}\t b`, '\ta\t\nb'); -test('$ handles tokens - \\n (no escape)', testScriptStdout, () => $`echo.js - `, ''); -test('$ handles expressions - \\n (no escape)', testScriptStdout, () => $`echo.js ${` -`} `, '\n'); -test('$ splits tokens - \\n (no escape)', testScriptStdout, () => $`echo.js a - b`, 'a\nb'); -test('$ splits expressions - \\n (no escape)', testScriptStdout, () => $`echo.js ${'a'} - ${'b'}`, 'a\nb'); -test('$ concatenates tokens - \\n (no escape)', testScriptStdout, () => $`echo.js -a - b`, 'a\nb'); -test('$ concatenates expressions - \\n (no escape)', testScriptStdout, () => $`echo.js -${'a'} - b`, 'a\nb'); -test('$ handles tokens - \\n (escape)', testScriptStdout, () => $`echo.js \n `, '\n'); -test('$ handles expressions - \\n (escape)', testScriptStdout, () => $`echo.js ${'\n'} `, '\n'); -test('$ splits tokens - \\n (escape)', testScriptStdout, () => $`echo.js a\n b`, 'a\n\nb'); -test('$ splits expressions - \\n (escape)', testScriptStdout, () => $`echo.js ${'a'}\n ${'b'}`, 'a\n\nb'); -test('$ concatenates tokens - \\n (escape)', testScriptStdout, () => $`echo.js \na\n b`, '\na\n\nb'); -test('$ concatenates expressions - \\n (escape)', testScriptStdout, () => $`echo.js \n${'a'}\n b`, '\na\n\nb'); -test('$ handles tokens - \\r (no escape)', testScriptStdout, () => escapedCall('echo.js \r '), ''); -test('$ splits tokens - \\r (no escape)', testScriptStdout, () => escapedCall('echo.js a\rb'), 'a\nb'); -test('$ splits expressions - \\r (no escape)', testScriptStdout, () => escapedCall(`echo.js ${'a'}\r${'b'}`), 'a\nb'); -test('$ concatenates tokens - \\r (no escape)', testScriptStdout, () => escapedCall('echo.js \ra\r b'), 'a\nb'); -test('$ concatenates expressions - \\r (no escape)', testScriptStdout, () => escapedCall(`echo.js \r${'a'}\r b`), 'a\nb'); -test('$ splits tokens - \\r (escape)', testScriptStdout, () => $`echo.js a\r b`, 'a\r\nb'); -test('$ splits expressions - \\r (escape)', testScriptStdout, () => $`echo.js ${'a'}\r ${'b'}`, 'a\r\nb'); -test('$ concatenates tokens - \\r (escape)', testScriptStdout, () => $`echo.js \ra\r b`, '\ra\r\nb'); -test('$ concatenates expressions - \\r (escape)', testScriptStdout, () => $`echo.js \r${'a'}\r b`, '\ra\r\nb'); -test('$ handles tokens - \\r\\n (no escape)', testScriptStdout, () => escapedCall('echo.js \r\n '), ''); -test('$ splits tokens - \\r\\n (no escape)', testScriptStdout, () => escapedCall('echo.js a\r\nb'), 'a\nb'); -test('$ splits expressions - \\r\\n (no escape)', testScriptStdout, () => escapedCall(`echo.js ${'a'}\r\n${'b'}`), 'a\nb'); -test('$ concatenates tokens - \\r\\n (no escape)', testScriptStdout, () => escapedCall('echo.js \r\na\r\n b'), 'a\nb'); -test('$ concatenates expressions - \\r\\n (no escape)', testScriptStdout, () => escapedCall(`echo.js \r\n${'a'}\r\n b`), 'a\nb'); -test('$ handles tokens - \\r\\n (escape)', testScriptStdout, () => $`echo.js \r\n `, '\r\n'); -test('$ handles expressions - \\r\\n (escape)', testScriptStdout, () => $`echo.js ${'\r\n'} `, '\r\n'); -test('$ splits tokens - \\r\\n (escape)', testScriptStdout, () => $`echo.js a\r\n b`, 'a\r\n\nb'); -test('$ splits expressions - \\r\\n (escape)', testScriptStdout, () => $`echo.js ${'a'}\r\n ${'b'}`, 'a\r\n\nb'); -test('$ concatenates tokens - \\r\\n (escape)', testScriptStdout, () => $`echo.js \r\na\r\n b`, '\r\na\r\n\nb'); -test('$ concatenates expressions - \\r\\n (escape)', testScriptStdout, () => $`echo.js \r\n${'a'}\r\n b`, '\r\na\r\n\nb'); -/* eslint-disable no-irregular-whitespace */ -test('$ handles expressions - \\f (no escape)', testScriptStdout, () => $`echo.js ${' '}`, '\f'); -test('$ splits tokens - \\f (no escape)', testScriptStdout, () => $`echo.js a b`, 'a\fb'); -test('$ splits expressions - \\f (no escape)', testScriptStdout, () => $`echo.js ${'a'} ${'b'}`, 'a\fb'); -test('$ concatenates tokens - \\f (no escape)', testScriptStdout, () => $`echo.js a b`, '\fa\f\nb'); -test('$ concatenates expressions - \\f (no escape)', testScriptStdout, () => $`echo.js ${'a'} b`, '\fa\f\nb'); -/* eslint-enable no-irregular-whitespace */ -test('$ handles tokens - \\f (escape)', testScriptStdout, () => $`echo.js \f`, '\f'); -test('$ handles expressions - \\f (escape)', testScriptStdout, () => $`echo.js ${'\f'}`, '\f'); -test('$ splits tokens - \\f (escape)', testScriptStdout, () => $`echo.js a\fb`, 'a\fb'); -test('$ splits expressions - \\f (escape)', testScriptStdout, () => $`echo.js ${'a'}\f${'b'}`, 'a\fb'); -test('$ concatenates tokens - \\f (escape)', testScriptStdout, () => $`echo.js \fa\f b`, '\fa\f\nb'); -test('$ concatenates expressions - \\f (escape)', testScriptStdout, () => $`echo.js \f${'a'}\f b`, '\fa\f\nb'); -test('$ handles tokens - \\', testScriptStdout, () => $`echo.js \\`, '\\'); -test('$ handles expressions - \\', testScriptStdout, () => $`echo.js ${'\\'}`, '\\'); -test('$ splits tokens - \\', testScriptStdout, () => $`echo.js a\\b`, 'a\\b'); -test('$ splits expressions - \\', testScriptStdout, () => $`echo.js ${'a'}\\${'b'}`, 'a\\b'); -test('$ concatenates tokens - \\', testScriptStdout, () => $`echo.js \\a\\ b`, '\\a\\\nb'); -test('$ concatenates expressions - \\', testScriptStdout, () => $`echo.js \\${'a'}\\ b`, '\\a\\\nb'); -test('$ handles tokens - \\\\', testScriptStdout, () => $`echo.js \\\\`, '\\\\'); -test('$ handles expressions - \\\\', testScriptStdout, () => $`echo.js ${'\\\\'}`, '\\\\'); -test('$ splits tokens - \\\\', testScriptStdout, () => $`echo.js a\\\\b`, 'a\\\\b'); -test('$ splits expressions - \\\\', testScriptStdout, () => $`echo.js ${'a'}\\\\${'b'}`, 'a\\\\b'); -test('$ concatenates tokens - \\\\', testScriptStdout, () => $`echo.js \\\\a\\\\ b`, '\\\\a\\\\\nb'); -test('$ concatenates expressions - \\\\', testScriptStdout, () => $`echo.js \\\\${'a'}\\\\ b`, '\\\\a\\\\\nb'); -test('$ handles tokens - `', testScriptStdout, () => $`echo.js \``, '`'); -test('$ handles expressions - `', testScriptStdout, () => $`echo.js ${'`'}`, '`'); -test('$ splits tokens - `', testScriptStdout, () => $`echo.js a\`b`, 'a`b'); -test('$ splits expressions - `', testScriptStdout, () => $`echo.js ${'a'}\`${'b'}`, 'a`b'); -test('$ concatenates tokens - `', testScriptStdout, () => $`echo.js \`a\` b`, '`a`\nb'); -test('$ concatenates expressions - `', testScriptStdout, () => $`echo.js \`${'a'}\` b`, '`a`\nb'); -test('$ handles tokens - \\v', testScriptStdout, () => $`echo.js \v`, '\v'); -test('$ handles expressions - \\v', testScriptStdout, () => $`echo.js ${'\v'}`, '\v'); -test('$ splits tokens - \\v', testScriptStdout, () => $`echo.js a\vb`, 'a\vb'); -test('$ splits expressions - \\v', testScriptStdout, () => $`echo.js ${'a'}\v${'b'}`, 'a\vb'); -test('$ concatenates tokens - \\v', testScriptStdout, () => $`echo.js \va\v b`, '\va\v\nb'); -test('$ concatenates expressions - \\v', testScriptStdout, () => $`echo.js \v${'a'}\v b`, '\va\v\nb'); -test('$ handles tokens - \\u2028', testScriptStdout, () => $`echo.js \u2028`, '\u2028'); -test('$ handles expressions - \\u2028', testScriptStdout, () => $`echo.js ${'\u2028'}`, '\u2028'); -test('$ splits tokens - \\u2028', testScriptStdout, () => $`echo.js a\u2028b`, 'a\u2028b'); -test('$ splits expressions - \\u2028', testScriptStdout, () => $`echo.js ${'a'}\u2028${'b'}`, 'a\u2028b'); -test('$ concatenates tokens - \\u2028', testScriptStdout, () => $`echo.js \u2028a\u2028 b`, '\u2028a\u2028\nb'); -test('$ concatenates expressions - \\u2028', testScriptStdout, () => $`echo.js \u2028${'a'}\u2028 b`, '\u2028a\u2028\nb'); -test('$ handles tokens - \\a', testScriptStdout, () => $`echo.js \a`, 'a'); -test('$ splits tokens - \\a', testScriptStdout, () => $`echo.js a\ab`, 'aab'); -test('$ splits expressions - \\a', testScriptStdout, () => $`echo.js ${'a'}\a${'b'}`, 'aab'); -test('$ concatenates tokens - \\a', testScriptStdout, () => $`echo.js \aa\a b`, 'aaa\nb'); -test('$ concatenates expressions - \\a', testScriptStdout, () => $`echo.js \a${'a'}\a b`, 'aaa\nb'); -test('$ handles tokens - \\cJ', testScriptStdout, () => $`echo.js \cJ`, 'cJ'); -test('$ splits tokens - \\cJ', testScriptStdout, () => $`echo.js a\cJb`, 'acJb'); -test('$ splits expressions - \\cJ', testScriptStdout, () => $`echo.js ${'a'}\cJ${'b'}`, 'acJb'); -test('$ concatenates tokens - \\cJ', testScriptStdout, () => $`echo.js \cJa\cJ b`, 'cJacJ\nb'); -test('$ concatenates expressions - \\cJ', testScriptStdout, () => $`echo.js \cJ${'a'}\cJ b`, 'cJacJ\nb'); -test('$ handles tokens - \\.', testScriptStdout, () => $`echo.js \.`, '.'); -test('$ splits tokens - \\.', testScriptStdout, () => $`echo.js a\.b`, 'a.b'); -test('$ splits expressions - \\.', testScriptStdout, () => $`echo.js ${'a'}\.${'b'}`, 'a.b'); -test('$ concatenates tokens - \\.', testScriptStdout, () => $`echo.js \.a\. b`, '.a.\nb'); -test('$ concatenates expressions - \\.', testScriptStdout, () => $`echo.js \.${'a'}\. b`, '.a.\nb'); -/* eslint-disable unicorn/no-hex-escape */ -test('$ handles tokens - \\x63', testScriptStdout, () => $`echo.js \x63`, 'c'); -test('$ splits tokens - \\x63', testScriptStdout, () => $`echo.js a\x63b`, 'acb'); -test('$ splits expressions - \\x63', testScriptStdout, () => $`echo.js ${'a'}\x63${'b'}`, 'acb'); -test('$ concatenates tokens - \\x63', testScriptStdout, () => $`echo.js \x63a\x63 b`, 'cac\nb'); -test('$ concatenates expressions - \\x63', testScriptStdout, () => $`echo.js \x63${'a'}\x63 b`, 'cac\nb'); -/* eslint-enable unicorn/no-hex-escape */ -test('$ handles tokens - \\u0063', testScriptStdout, () => $`echo.js \u0063`, 'c'); -test('$ splits tokens - \\u0063', testScriptStdout, () => $`echo.js a\u0063b`, 'acb'); -test('$ splits expressions - \\u0063', testScriptStdout, () => $`echo.js ${'a'}\u0063${'b'}`, 'acb'); -test('$ concatenates tokens - \\u0063', testScriptStdout, () => $`echo.js \u0063a\u0063 b`, 'cac\nb'); -test('$ concatenates expressions - \\u0063', testScriptStdout, () => $`echo.js \u0063${'a'}\u0063 b`, 'cac\nb'); -test('$ handles tokens - \\u{1}', testScriptStdout, () => $`echo.js \u{1}`, '\u0001'); -test('$ splits tokens - \\u{1}', testScriptStdout, () => $`echo.js a\u{1}b`, 'a\u0001b'); -test('$ splits expressions - \\u{1}', testScriptStdout, () => $`echo.js ${'a'}\u{1}${'b'}`, 'a\u0001b'); -test('$ concatenates tokens - \\u{1}', testScriptStdout, () => $`echo.js \u{1}a\u{1} b`, '\u0001a\u0001\nb'); -test('$ concatenates expressions - \\u{1}', testScriptStdout, () => $`echo.js \u{1}${'a'}\u{1} b`, '\u0001a\u0001\nb'); -test('$ handles tokens - \\u{63}', testScriptStdout, () => $`echo.js \u{63}`, 'c'); -test('$ splits tokens - \\u{63}', testScriptStdout, () => $`echo.js a\u{63}b`, 'acb'); -test('$ splits expressions - \\u{63}', testScriptStdout, () => $`echo.js ${'a'}\u{63}${'b'}`, 'acb'); -test('$ concatenates tokens - \\u{63}', testScriptStdout, () => $`echo.js \u{63}a\u{63} b`, 'cac\nb'); -test('$ concatenates expressions - \\u{63}', testScriptStdout, () => $`echo.js \u{63}${'a'}\u{63} b`, 'cac\nb'); -test('$ handles tokens - \\u{063}', testScriptStdout, () => $`echo.js \u{063}`, 'c'); -test('$ splits tokens - \\u{063}', testScriptStdout, () => $`echo.js a\u{063}b`, 'acb'); -test('$ splits expressions - \\u{063}', testScriptStdout, () => $`echo.js ${'a'}\u{063}${'b'}`, 'acb'); -test('$ concatenates tokens - \\u{063}', testScriptStdout, () => $`echo.js \u{063}a\u{063} b`, 'cac\nb'); -test('$ concatenates expressions - \\u{063}', testScriptStdout, () => $`echo.js \u{063}${'a'}\u{063} b`, 'cac\nb'); -test('$ handles tokens - \\u{0063}', testScriptStdout, () => $`echo.js \u{0063}`, 'c'); -test('$ splits tokens - \\u{0063}', testScriptStdout, () => $`echo.js a\u{0063}b`, 'acb'); -test('$ splits expressions - \\u{0063}', testScriptStdout, () => $`echo.js ${'a'}\u{0063}${'b'}`, 'acb'); -test('$ concatenates tokens - \\u{0063}', testScriptStdout, () => $`echo.js \u{0063}a\u{0063} b`, 'cac\nb'); -test('$ concatenates expressions - \\u{0063}', testScriptStdout, () => $`echo.js \u{0063}${'a'}\u{0063} b`, 'cac\nb'); -test('$ handles tokens - \\u{00063}', testScriptStdout, () => $`echo.js \u{00063}`, 'c'); -test('$ splits tokens - \\u{00063}', testScriptStdout, () => $`echo.js a\u{00063}b`, 'acb'); -test('$ splits expressions - \\u{00063}', testScriptStdout, () => $`echo.js ${'a'}\u{00063}${'b'}`, 'acb'); -test('$ concatenates tokens - \\u{00063}', testScriptStdout, () => $`echo.js \u{00063}a\u{00063} b`, 'cac\nb'); -test('$ concatenates expressions - \\u{00063}', testScriptStdout, () => $`echo.js \u{00063}${'a'}\u{00063} b`, 'cac\nb'); -test('$ handles tokens - \\u{000063}', testScriptStdout, () => $`echo.js \u{000063}`, 'c'); -test('$ splits tokens - \\u{000063}', testScriptStdout, () => $`echo.js a\u{000063}b`, 'acb'); -test('$ splits expressions - \\u{000063}', testScriptStdout, () => $`echo.js ${'a'}\u{000063}${'b'}`, 'acb'); -test('$ concatenates tokens - \\u{000063}', testScriptStdout, () => $`echo.js \u{000063}a\u{000063} b`, 'cac\nb'); -test('$ concatenates expressions - \\u{000063}', testScriptStdout, () => $`echo.js \u{000063}${'a'}\u{000063} b`, 'cac\nb'); -test('$ handles tokens - \\u{0000063}', testScriptStdout, () => $`echo.js \u{0000063}`, 'c'); -test('$ splits tokens - \\u{0000063}', testScriptStdout, () => $`echo.js a\u{0000063}b`, 'acb'); -test('$ splits expressions - \\u{0000063}', testScriptStdout, () => $`echo.js ${'a'}\u{0000063}${'b'}`, 'acb'); -test('$ concatenates tokens - \\u{0000063}', testScriptStdout, () => $`echo.js \u{0000063}a\u{0000063} b`, 'cac\nb'); -test('$ concatenates expressions - \\u{0000063}', testScriptStdout, () => $`echo.js \u{0000063}${'a'}\u{0000063} b`, 'cac\nb'); -test('$ handles tokens - \\u{0063}}', testScriptStdout, () => $`echo.js \u{0063}}`, 'c}'); -test('$ splits tokens - \\u{0063}}', testScriptStdout, () => $`echo.js a\u{0063}}b`, 'ac}b'); -test('$ splits expressions - \\u{0063}}', testScriptStdout, () => $`echo.js ${'a'}\u{0063}}${'b'}`, 'ac}b'); -test('$ concatenates tokens - \\u{0063}}', testScriptStdout, () => $`echo.js \u{0063}}a\u{0063}} b`, 'c}ac}\nb'); -test('$ concatenates expressions - \\u{0063}}', testScriptStdout, () => $`echo.js \u{0063}}${'a'}\u{0063}} b`, 'c}ac}\nb'); - -const testScriptErrorStdout = async (t, getSubprocess) => { - t.throws(getSubprocess, {message: /null bytes/}); -}; - -test('$ handles tokens - \\0', testScriptErrorStdout, () => $`echo.js \0`); -test('$ splits tokens - \\0', testScriptErrorStdout, () => $`echo.js a\0b`); -test('$ splits expressions - \\0', testScriptErrorStdout, () => $`echo.js ${'a'}\0${'b'}`); -test('$ concatenates tokens - \\0', testScriptErrorStdout, () => $`echo.js \0a\0 b`); -test('$ concatenates expressions - \\0', testScriptErrorStdout, () => $`echo.js \0${'a'}\0 b`); - -const testReturnInterpolate = async (t, getSubprocess, expectedStdout, options = {}) => { - const foo = await $(options)`echo.js foo`; - const {stdout} = await getSubprocess(foo); - t.is(stdout, expectedStdout); -}; - -test('$ allows execa return value interpolation', testReturnInterpolate, foo => $`echo.js ${foo} bar`, 'foo\nbar'); -test('$ allows execa return value buffer interpolation', testReturnInterpolate, foo => $`echo.js ${foo} bar`, 'foo\nbar', {encoding: 'buffer'}); -test('$ allows execa return value array interpolation', testReturnInterpolate, foo => $`echo.js ${[foo, 'bar']}`, 'foo\nbar'); -test('$ allows execa return value buffer array interpolation', testReturnInterpolate, foo => $`echo.js ${[foo, 'bar']}`, 'foo\nbar', {encoding: 'buffer'}); - -const testReturnInterpolateSync = (t, getSubprocess, expectedStdout, options = {}) => { - const foo = $(options).sync`echo.js foo`; - const {stdout} = getSubprocess(foo); - t.is(stdout, expectedStdout); -}; - -test('$.sync allows execa return value interpolation', testReturnInterpolateSync, foo => $.sync`echo.js ${foo} bar`, 'foo\nbar'); -test('$.sync allows execa return value buffer interpolation', testReturnInterpolateSync, foo => $.sync`echo.js ${foo} bar`, 'foo\nbar', {encoding: 'buffer'}); -test('$.sync allows execa return value array interpolation', testReturnInterpolateSync, foo => $.sync`echo.js ${[foo, 'bar']}`, 'foo\nbar'); -test('$.sync allows execa return value buffer array interpolation', testReturnInterpolateSync, foo => $.sync`echo.js ${[foo, 'bar']}`, 'foo\nbar', {encoding: 'buffer'}); - -const testInvalidSequence = (t, getSubprocess) => { - t.throws(getSubprocess, {message: /Invalid backslash sequence/}); -}; - -test('$ handles invalid escape sequence - \\1', testInvalidSequence, () => $`echo.js \1`); -test('$ handles invalid escape sequence - \\u', testInvalidSequence, () => $`echo.js \u`); -test('$ handles invalid escape sequence - \\u0', testInvalidSequence, () => $`echo.js \u0`); -test('$ handles invalid escape sequence - \\u00', testInvalidSequence, () => $`echo.js \u00`); -test('$ handles invalid escape sequence - \\u000', testInvalidSequence, () => $`echo.js \u000`); -test('$ handles invalid escape sequence - \\ug', testInvalidSequence, () => $`echo.js \ug`); -test('$ handles invalid escape sequence - \\u{', testInvalidSequence, () => $`echo.js \u{`); -test('$ handles invalid escape sequence - \\u{0000', testInvalidSequence, () => $`echo.js \u{0000`); -test('$ handles invalid escape sequence - \\u{g}', testInvalidSequence, () => $`echo.js \u{g}`); -/* eslint-disable unicorn/no-hex-escape */ -test('$ handles invalid escape sequence - \\x', testInvalidSequence, () => $`echo.js \x`); -test('$ handles invalid escape sequence - \\x0', testInvalidSequence, () => $`echo.js \x0`); -test('$ handles invalid escape sequence - \\xgg', testInvalidSequence, () => $`echo.js \xgg`); -/* eslint-enable unicorn/no-hex-escape */ - -const testEmptyScript = (t, getSubprocess) => { - t.throws(getSubprocess, {message: /Template script must not be empty/}); -}; - -test('$``', testEmptyScript, () => $``); -test('$` `', testEmptyScript, () => $` `); -test('$` ` (2 spaces)', testEmptyScript, () => $` `); -test('$`\\t`', testEmptyScript, () => $` `); -test('$`\\n`', testEmptyScript, () => $` -`); - -const testInvalidExpression = (t, invalidExpression) => { - t.throws( - () => $`echo.js ${invalidExpression}`, - {message: /in template expression/}, - ); -}; - -test('$ throws on invalid expression - undefined', testInvalidExpression, undefined); -test('$ throws on invalid expression - null', testInvalidExpression, null); -test('$ throws on invalid expression - true', testInvalidExpression, true); -test('$ throws on invalid expression - {}', testInvalidExpression, {}); -test('$ throws on invalid expression - {foo: "bar"}', testInvalidExpression, {foo: 'bar'}); -test('$ throws on invalid expression - {stdout: 1}', testInvalidExpression, {stdout: 1}); -test('$ throws on invalid expression - [undefined]', testInvalidExpression, [undefined]); -test('$ throws on invalid expression - [null]', testInvalidExpression, [null]); -test('$ throws on invalid expression - [true]', testInvalidExpression, [true]); -test('$ throws on invalid expression - [{}]', testInvalidExpression, [{}]); -test('$ throws on invalid expression - [{foo: "bar"}]', testInvalidExpression, [{foo: 'bar'}]); -test('$ throws on invalid expression - [{stdout: 1}]', testInvalidExpression, [{stdout: 1}]); - -const testMissingOutput = (t, invalidExpression) => { - t.throws( - () => $`echo.js ${invalidExpression()}`, - {message: /Missing result.stdout/}, - ); -}; - -test('$ throws on invalid expression - {stdout: undefined}', testMissingOutput, () => ({stdout: undefined})); -test('$ throws on invalid expression - [{stdout: undefined}]', testMissingOutput, () => [{stdout: undefined}]); -test('$ throws on invalid expression - $(options).sync', testMissingOutput, () => $({stdio: 'ignore'}).sync`noop.js`); -test('$ throws on invalid expression - [$(options).sync]', testMissingOutput, () => [$({stdio: 'ignore'}).sync`noop.js`]); - -const testInvalidPromise = (t, invalidExpression) => { - t.throws( - () => $`echo.js ${invalidExpression()}`, - {message: /Please use \${await subprocess}/}, - ); -}; - -test('$ throws on invalid expression - Promise.resolve()', testInvalidPromise, async () => ({})); -test('$ throws on invalid expression - Promise.resolve({stdout: "foo"})', testInvalidPromise, async () => ({foo: 'bar'})); -test('$ throws on invalid expression - [Promise.resolve()]', testInvalidPromise, () => [Promise.resolve()]); -test('$ throws on invalid expression - [Promise.resolve({stdout: "foo"})]', testInvalidPromise, () => [Promise.resolve({stdout: 'foo'})]); -test('$ throws on invalid expression - $', testInvalidPromise, () => $`noop.js`); -test('$ throws on invalid expression - [$]', testInvalidPromise, () => [$`noop.js`]); diff --git a/test/pipe/abort.js b/test/pipe/abort.js deleted file mode 100644 index 5be595c056..0000000000 --- a/test/pipe/abort.js +++ /dev/null @@ -1,167 +0,0 @@ -import {once} from 'node:events'; -import test from 'ava'; -import {execa} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString} from '../helpers/input.js'; - -setFixtureDirectory(); - -const assertUnPipeError = async (t, pipePromise) => { - const error = await t.throwsAsync(pipePromise); - - t.is(error.command, 'source.pipe(destination)'); - t.is(error.escapedCommand, error.command); - - t.is(typeof error.cwd, 'string'); - t.true(error.failed); - t.false(error.timedOut); - t.false(error.isCanceled); - t.false(error.isTerminated); - t.is(error.exitCode, undefined); - t.is(error.signal, undefined); - t.is(error.signalDescription, undefined); - t.is(error.stdout, undefined); - t.is(error.stderr, undefined); - t.is(error.all, undefined); - t.deepEqual(error.stdio, Array.from({length: error.stdio.length})); - t.deepEqual(error.pipedFrom, []); - - t.true(error.originalMessage.includes('Pipe canceled')); - t.true(error.shortMessage.includes(`Command failed: ${error.command}`)); - t.true(error.shortMessage.includes(error.originalMessage)); - t.true(error.message.includes(error.shortMessage)); -}; - -test('Can unpipe a single subprocess', async t => { - const abortController = new AbortController(); - const source = execa('stdin.js'); - const destination = execa('stdin.js'); - const pipePromise = source.pipe(destination, {unpipeSignal: abortController.signal}); - - abortController.abort(); - await assertUnPipeError(t, pipePromise); - - source.stdin.end(foobarString); - destination.stdin.end('.'); - - t.like(await destination, {stdout: '.'}); - t.like(await source, {stdout: foobarString}); -}); - -test('Can use an already aborted signal', async t => { - const abortController = new AbortController(); - abortController.abort(); - const source = execa('empty.js'); - const destination = execa('empty.js'); - const pipePromise = source.pipe(destination, {unpipeSignal: abortController.signal}); - - await assertUnPipeError(t, pipePromise); -}); - -test('Can unpipe a subprocess among other sources', async t => { - const abortController = new AbortController(); - const source = execa('stdin.js'); - const secondSource = execa('noop.js', [foobarString]); - const destination = execa('stdin.js'); - const pipePromise = source.pipe(destination, {unpipeSignal: abortController.signal}); - const secondPipePromise = secondSource.pipe(destination); - - abortController.abort(); - await assertUnPipeError(t, pipePromise); - - source.stdin.end('.'); - - t.is(await secondPipePromise, await destination); - t.like(await destination, {stdout: foobarString}); - t.like(await source, {stdout: '.'}); - t.like(await secondSource, {stdout: foobarString}); -}); - -test('Can unpipe a subprocess among other sources on the same subprocess', async t => { - const abortController = new AbortController(); - const source = execa('stdin-both.js'); - const destination = execa('stdin.js'); - const pipePromise = source.pipe(destination, {unpipeSignal: abortController.signal}); - const secondPipePromise = source.pipe(destination, {from: 'stderr'}); - - abortController.abort(); - await assertUnPipeError(t, pipePromise); - - source.stdin.end(foobarString); - - t.is(await secondPipePromise, await destination); - t.like(await destination, {stdout: foobarString}); - t.like(await source, {stdout: foobarString, stderr: foobarString}); -}); - -test('Can unpipe a subprocess among other destinations', async t => { - const abortController = new AbortController(); - const source = execa('stdin.js'); - const destination = execa('stdin.js'); - const secondDestination = execa('stdin.js'); - const pipePromise = source.pipe(destination, {unpipeSignal: abortController.signal}); - const secondPipePromise = source.pipe(secondDestination); - - abortController.abort(); - await assertUnPipeError(t, pipePromise); - - source.stdin.end(foobarString); - destination.stdin.end('.'); - - t.is(await secondPipePromise, await secondDestination); - t.like(await destination, {stdout: '.'}); - t.like(await source, {stdout: foobarString}); - t.like(await secondDestination, {stdout: foobarString}); -}); - -test('Can unpipe then re-pipe a subprocess', async t => { - const abortController = new AbortController(); - const source = execa('stdin.js'); - const destination = execa('stdin.js'); - const pipePromise = source.pipe(destination, {unpipeSignal: abortController.signal}); - - source.stdin.write('.'); - const [firstWrite] = await once(source.stdout, 'data'); - t.is(firstWrite.toString(), '.'); - - abortController.abort(); - await assertUnPipeError(t, pipePromise); - - source.pipe(destination); - source.stdin.end('.'); - - t.like(await destination, {stdout: '..'}); - t.like(await source, {stdout: '..'}); -}); - -test('Can unpipe to prevent termination to propagate to source', async t => { - const abortController = new AbortController(); - const source = execa('stdin.js'); - const destination = execa('stdin.js'); - const pipePromise = source.pipe(destination, {unpipeSignal: abortController.signal}); - - abortController.abort(); - await assertUnPipeError(t, pipePromise); - - destination.kill(); - t.like(await t.throwsAsync(destination), {signal: 'SIGTERM'}); - - source.stdin.end(foobarString); - t.like(await source, {stdout: foobarString}); -}); - -test('Can unpipe to prevent termination to propagate to destination', async t => { - const abortController = new AbortController(); - const source = execa('noop-forever.js', [foobarString]); - const destination = execa('stdin.js'); - const pipePromise = source.pipe(destination, {unpipeSignal: abortController.signal}); - - abortController.abort(); - await assertUnPipeError(t, pipePromise); - - source.kill(); - t.like(await t.throwsAsync(source), {signal: 'SIGTERM'}); - - destination.stdin.end(foobarString); - t.like(await destination, {stdout: foobarString}); -}); diff --git a/test/pipe/pipe-arguments.js b/test/pipe/pipe-arguments.js deleted file mode 100644 index 291eb0728f..0000000000 --- a/test/pipe/pipe-arguments.js +++ /dev/null @@ -1,272 +0,0 @@ -import {spawn} from 'node:child_process'; -import {pathToFileURL} from 'node:url'; -import test from 'ava'; -import {$, execa} from '../../index.js'; -import {setFixtureDirectory, FIXTURES_DIRECTORY} from '../helpers/fixtures-directory.js'; -import {foobarString} from '../helpers/input.js'; - -setFixtureDirectory(); - -test('$.pipe(subprocess)', async t => { - const {stdout} = await $`noop.js ${foobarString}`.pipe($({stdin: 'pipe'})`stdin.js`); - t.is(stdout, foobarString); -}); - -test('execa.$.pipe(subprocess)', async t => { - const {stdout} = await execa('noop.js', [foobarString]).pipe($({stdin: 'pipe'})`stdin.js`); - t.is(stdout, foobarString); -}); - -test('$.pipe.pipe(subprocess)', async t => { - const {stdout} = await $`noop.js ${foobarString}` - .pipe($({stdin: 'pipe'})`stdin.js`) - .pipe($({stdin: 'pipe'})`stdin.js`); - t.is(stdout, foobarString); -}); - -test('$.pipe`command`', async t => { - const {stdout} = await $`noop.js ${foobarString}`.pipe`stdin.js`; - t.is(stdout, foobarString); -}); - -test('execa.$.pipe`command`', async t => { - const {stdout} = await execa('noop.js', [foobarString]).pipe`stdin.js`; - t.is(stdout, foobarString); -}); - -test('$.pipe.pipe`command`', async t => { - const {stdout} = await $`noop.js ${foobarString}` - .pipe`stdin.js` - .pipe`stdin.js`; - t.is(stdout, foobarString); -}); - -test('$.pipe("file")', async t => { - const {stdout} = await $`noop.js ${foobarString}`.pipe('stdin.js'); - t.is(stdout, foobarString); -}); - -test('execa.$.pipe("file")`', async t => { - const {stdout} = await execa('noop.js', [foobarString]).pipe('stdin.js'); - t.is(stdout, foobarString); -}); - -test('$.pipe.pipe("file")', async t => { - const {stdout} = await $`noop.js ${foobarString}` - .pipe`stdin.js` - .pipe('stdin.js'); - t.is(stdout, foobarString); -}); - -test('execa.$.pipe(fileUrl)`', async t => { - const {stdout} = await execa('noop.js', [foobarString]).pipe(pathToFileURL(`${FIXTURES_DIRECTORY}/stdin.js`)); - t.is(stdout, foobarString); -}); - -test('$.pipe("file", commandArguments, options)', async t => { - const {stdout} = await $`noop.js ${foobarString}`.pipe('node', ['stdin.js'], {cwd: FIXTURES_DIRECTORY}); - t.is(stdout, foobarString); -}); - -test('execa.$.pipe("file", commandArguments, options)`', async t => { - const {stdout} = await execa('noop.js', [foobarString]).pipe('node', ['stdin.js'], {cwd: FIXTURES_DIRECTORY}); - t.is(stdout, foobarString); -}); - -test('$.pipe.pipe("file", commandArguments, options)', async t => { - const {stdout} = await $`noop.js ${foobarString}` - .pipe`stdin.js` - .pipe('node', ['stdin.js'], {cwd: FIXTURES_DIRECTORY}); - t.is(stdout, foobarString); -}); - -test('$.pipe(subprocess, pipeOptions)', async t => { - const {stdout} = await $`noop-fd.js 2 ${foobarString}`.pipe($({stdin: 'pipe'})`stdin.js`, {from: 'stderr'}); - t.is(stdout, foobarString); -}); - -test('execa.$.pipe(subprocess, pipeOptions)', async t => { - const {stdout} = await execa('noop-fd.js', ['2', foobarString]).pipe($({stdin: 'pipe'})`stdin.js`, {from: 'stderr'}); - t.is(stdout, foobarString); -}); - -test('$.pipe.pipe(subprocess, pipeOptions)', async t => { - const {stdout} = await $`noop-fd.js 2 ${foobarString}` - .pipe($({stdin: 'pipe'})`noop-stdin-fd.js 2`, {from: 'stderr'}) - .pipe($({stdin: 'pipe'})`stdin.js`, {from: 'stderr'}); - t.is(stdout, foobarString); -}); - -test('$.pipe(pipeOptions)`command`', async t => { - const {stdout} = await $`noop-fd.js 2 ${foobarString}`.pipe({from: 'stderr'})`stdin.js`; - t.is(stdout, foobarString); -}); - -test('execa.$.pipe(pipeOptions)`command`', async t => { - const {stdout} = await execa('noop-fd.js', ['2', foobarString]).pipe({from: 'stderr'})`stdin.js`; - t.is(stdout, foobarString); -}); - -test('$.pipe.pipe(pipeOptions)`command`', async t => { - const {stdout} = await $`noop-fd.js 2 ${foobarString}` - .pipe({from: 'stderr'})`noop-stdin-fd.js 2` - .pipe({from: 'stderr'})`stdin.js`; - t.is(stdout, foobarString); -}); - -test('$.pipe("file", pipeOptions)', async t => { - const {stdout} = await $`noop-fd.js 2 ${foobarString}`.pipe('stdin.js', {from: 'stderr'}); - t.is(stdout, foobarString); -}); - -test('execa.$.pipe("file", pipeOptions)', async t => { - const {stdout} = await execa('noop-fd.js', ['2', foobarString]).pipe('stdin.js', {from: 'stderr'}); - t.is(stdout, foobarString); -}); - -test('$.pipe.pipe("file", pipeOptions)', async t => { - const {stdout} = await $`noop-fd.js 2 ${foobarString}` - .pipe({from: 'stderr'})`noop-stdin-fd.js 2` - .pipe('stdin.js', {from: 'stderr'}); - t.is(stdout, foobarString); -}); - -test('$.pipe(options)`command`', async t => { - const {stdout} = await $`noop.js ${foobarString}`.pipe({stripFinalNewline: false})`stdin.js`; - t.is(stdout, `${foobarString}\n`); -}); - -test('execa.$.pipe(options)`command`', async t => { - const {stdout} = await execa('noop.js', [foobarString]).pipe({stripFinalNewline: false})`stdin.js`; - t.is(stdout, `${foobarString}\n`); -}); - -test('$.pipe.pipe(options)`command`', async t => { - const {stdout} = await $`noop.js ${foobarString}` - .pipe({})`stdin.js` - .pipe({stripFinalNewline: false})`stdin.js`; - t.is(stdout, `${foobarString}\n`); -}); - -test('$.pipe("file", options)', async t => { - const {stdout} = await $`noop.js ${foobarString}`.pipe('stdin.js', {stripFinalNewline: false}); - t.is(stdout, `${foobarString}\n`); -}); - -test('execa.$.pipe("file", options)', async t => { - const {stdout} = await execa('noop.js', [foobarString]).pipe('stdin.js', {stripFinalNewline: false}); - t.is(stdout, `${foobarString}\n`); -}); - -test('$.pipe.pipe("file", options)', async t => { - const {stdout} = await $`noop.js ${foobarString}` - .pipe({})`stdin.js` - .pipe('stdin.js', {stripFinalNewline: false}); - t.is(stdout, `${foobarString}\n`); -}); - -test('$.pipe(pipeAndSubprocessOptions)`command`', async t => { - const {stdout} = await $`noop-fd.js 2 ${foobarString}\n`.pipe({from: 'stderr', stripFinalNewline: false})`stdin.js`; - t.is(stdout, `${foobarString}\n`); -}); - -test('execa.$.pipe(pipeAndSubprocessOptions)`command`', async t => { - const {stdout} = await execa('noop-fd.js', ['2', `${foobarString}\n`]).pipe({from: 'stderr', stripFinalNewline: false})`stdin.js`; - t.is(stdout, `${foobarString}\n`); -}); - -test('$.pipe.pipe(pipeAndSubprocessOptions)`command`', async t => { - const {stdout} = await $`noop-fd.js 2 ${foobarString}\n` - .pipe({from: 'stderr'})`noop-stdin-fd.js 2` - .pipe({from: 'stderr', stripFinalNewline: false})`stdin.js`; - t.is(stdout, `${foobarString}\n`); -}); - -test('$.pipe("file", pipeAndSubprocessOptions)', async t => { - const {stdout} = await $`noop-fd.js 2 ${foobarString}\n`.pipe('stdin.js', {from: 'stderr', stripFinalNewline: false}); - t.is(stdout, `${foobarString}\n`); -}); - -test('execa.$.pipe("file", pipeAndSubprocessOptions)', async t => { - const {stdout} = await execa('noop-fd.js', ['2', `${foobarString}\n`]).pipe('stdin.js', {from: 'stderr', stripFinalNewline: false}); - t.is(stdout, `${foobarString}\n`); -}); - -test('$.pipe.pipe("file", pipeAndSubprocessOptions)', async t => { - const {stdout} = await $`noop-fd.js 2 ${foobarString}\n` - .pipe({from: 'stderr'})`noop-stdin-fd.js 2` - .pipe('stdin.js', {from: 'stderr', stripFinalNewline: false}); - t.is(stdout, `${foobarString}\n`); -}); - -test('$.pipe(options)(secondOptions)`command`', async t => { - const {stdout} = await $`noop.js ${foobarString}`.pipe({stripFinalNewline: false})({stripFinalNewline: true})`stdin.js`; - t.is(stdout, foobarString); -}); - -test('execa.$.pipe(options)(secondOptions)`command`', async t => { - const {stdout} = await execa('noop.js', [foobarString]).pipe({stripFinalNewline: false})({stripFinalNewline: true})`stdin.js`; - t.is(stdout, foobarString); -}); - -test('$.pipe.pipe(options)(secondOptions)`command`', async t => { - const {stdout} = await $`noop.js ${foobarString}` - .pipe({})({})`stdin.js` - .pipe({stripFinalNewline: false})({stripFinalNewline: true})`stdin.js`; - t.is(stdout, foobarString); -}); - -test('$.pipe`command` forces "stdin: pipe"', async t => { - const {stdout} = await $`noop.js ${foobarString}`.pipe({stdin: 'ignore'})`stdin.js`; - t.is(stdout, foobarString); -}); - -test('execa.pipe("file") forces "stdin: "pipe"', async t => { - const {stdout} = await execa('noop.js', [foobarString]).pipe('stdin.js', {stdin: 'ignore'}); - t.is(stdout, foobarString); -}); - -test('execa.pipe(subprocess) does not force "stdin: pipe"', async t => { - await t.throwsAsync( - execa('noop.js', [foobarString]).pipe(execa('stdin.js', {stdin: 'ignore'})), - {message: /"stdin: 'ignore'" option is incompatible/}, - ); -}); - -test('$.pipe(options)(subprocess) fails', async t => { - await t.throwsAsync( - $`empty.js`.pipe({stdout: 'pipe'})($`empty.js`), - {message: /Please use \.pipe/}, - ); -}); - -test('execa.$.pipe(options)(subprocess) fails', async t => { - await t.throwsAsync( - execa('empty.js').pipe({stdout: 'pipe'})($`empty.js`), - {message: /Please use \.pipe/}, - ); -}); - -test('$.pipe(options)("file") fails', async t => { - await t.throwsAsync( - $`empty.js`.pipe({stdout: 'pipe'})('empty.js'), - {message: /Please use \.pipe/}, - ); -}); - -test('execa.$.pipe(options)("file") fails', async t => { - await t.throwsAsync( - execa('empty.js').pipe({stdout: 'pipe'})('empty.js'), - {message: /Please use \.pipe/}, - ); -}); - -const testInvalidPipe = async (t, ...pipeArguments) => { - await t.throwsAsync( - $`empty.js`.pipe(...pipeArguments), - {message: /must be a template string/}, - ); -}; - -test('$.pipe(nonExecaSubprocess) fails', testInvalidPipe, spawn('node', ['--version'])); -test('$.pipe(false) fails', testInvalidPipe, false); diff --git a/test/pipe/sequence.js b/test/pipe/sequence.js deleted file mode 100644 index fef27e8487..0000000000 --- a/test/pipe/sequence.js +++ /dev/null @@ -1,313 +0,0 @@ -import {once} from 'node:events'; -import process from 'node:process'; -import {PassThrough} from 'node:stream'; -import test from 'ava'; -import {execa} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString} from '../helpers/input.js'; -import {noopGenerator} from '../helpers/generator.js'; -import {prematureClose} from '../helpers/stdio.js'; - -setFixtureDirectory(); - -const isLinux = process.platform === 'linux'; - -test('Source stream abort -> destination success', async t => { - const source = execa('noop-repeat.js'); - const destination = execa('stdin.js'); - const pipePromise = source.pipe(destination); - source.stdout.destroy(); - - t.is(await t.throwsAsync(pipePromise), await t.throwsAsync(source)); - t.like(await t.throwsAsync(source), {exitCode: 1}); - await destination; -}); - -test('Source stream error -> destination success', async t => { - const source = execa('noop-repeat.js'); - const destination = execa('stdin.js'); - const pipePromise = source.pipe(destination); - const cause = new Error('test'); - source.stdout.destroy(cause); - - t.is(await t.throwsAsync(pipePromise), await t.throwsAsync(source)); - t.like(await t.throwsAsync(source), {originalMessage: cause.message, exitCode: 1}); - await destination; -}); - -test('Destination stream abort -> source failure', async t => { - const source = execa('noop-repeat.js'); - const destination = execa('stdin.js'); - const pipePromise = source.pipe(destination); - destination.stdin.destroy(); - - t.is(await t.throwsAsync(pipePromise), await t.throwsAsync(destination)); - t.like(await t.throwsAsync(destination), prematureClose); - t.like(await t.throwsAsync(source), {exitCode: 1}); -}); - -test('Destination stream error -> source failure', async t => { - const source = execa('noop-repeat.js'); - const destination = execa('stdin.js'); - const pipePromise = source.pipe(destination); - const cause = new Error('test'); - destination.stdin.destroy(cause); - - t.is(await t.throwsAsync(pipePromise), await t.throwsAsync(destination)); - t.like(await t.throwsAsync(destination), {originalMessage: cause.message, exitCode: 0}); - t.like(await t.throwsAsync(source), {exitCode: 1}); -}); - -test('Source success -> destination success', async t => { - const source = execa('noop.js', [foobarString]); - const destination = execa('stdin.js'); - const pipePromise = source.pipe(destination); - - t.like(await source, {stdout: foobarString}); - t.like(await destination, {stdout: foobarString}); - t.is(await pipePromise, await destination); -}); - -test('Destination stream end -> source failure', async t => { - const source = execa('noop-repeat.js'); - const destination = execa('stdin.js'); - const pipePromise = source.pipe(destination); - destination.stdin.end(); - - t.is(await t.throwsAsync(pipePromise), await t.throwsAsync(source)); - await destination; - t.like(await t.throwsAsync(source), {exitCode: 1}); -}); - -test('Source normal failure -> destination success', async t => { - const source = execa('noop-fail.js', ['1', foobarString]); - const destination = execa('stdin.js'); - const pipePromise = source.pipe(destination); - - t.is(await t.throwsAsync(pipePromise), await t.throwsAsync(source)); - t.like(await t.throwsAsync(source), {stdout: foobarString, exitCode: 2}); - await destination; -}); - -test('Source normal failure -> deep destination success', async t => { - const source = execa('noop-fail.js', ['1', foobarString]); - const destination = execa('stdin.js'); - const secondDestination = execa('stdin.js'); - const pipePromise = source.pipe(destination); - const secondPipePromise = pipePromise.pipe(secondDestination); - - t.is(await t.throwsAsync(pipePromise), await t.throwsAsync(source)); - t.is(await t.throwsAsync(secondPipePromise), await t.throwsAsync(source)); - t.like(await t.throwsAsync(source), {stdout: foobarString, exitCode: 2}); - t.like(await destination, {stdout: foobarString}); - t.like(await secondDestination, {stdout: foobarString}); -}); - -const testSourceTerminated = async (t, signal) => { - const source = execa('noop-repeat.js'); - const destination = execa('stdin.js'); - const pipePromise = source.pipe(destination); - source.kill(signal); - - t.is(await t.throwsAsync(pipePromise), await t.throwsAsync(source)); - t.like(await t.throwsAsync(source), {signal}); - await destination; -}; - -test('Source SIGTERM -> destination success', testSourceTerminated, 'SIGTERM'); -test('Source SIGKILL -> destination success', testSourceTerminated, 'SIGKILL'); - -test('Destination success before source -> source success', async t => { - const passThroughStream = new PassThrough(); - const source = execa('stdin.js', {stdin: ['pipe', passThroughStream]}); - const destination = execa('empty.js'); - const pipePromise = source.pipe(destination); - - await destination; - passThroughStream.end(); - await source; - t.is(await pipePromise, await destination); -}); - -test('Destination normal failure -> source failure', async t => { - const source = execa('noop-repeat.js'); - const destination = execa('fail.js'); - const pipePromise = source.pipe(destination); - - t.is(await t.throwsAsync(pipePromise), await t.throwsAsync(destination)); - t.like(await t.throwsAsync(destination), {exitCode: 2}); - t.like(await t.throwsAsync(source), {exitCode: 1}); -}); - -test('Destination normal failure -> deep source failure', async t => { - const source = execa('noop-repeat.js'); - const destination = execa('stdin.js'); - const secondDestination = execa('fail.js'); - const pipePromise = source.pipe(destination); - const secondPipePromise = pipePromise.pipe(secondDestination); - - t.is(await t.throwsAsync(pipePromise), await t.throwsAsync(destination)); - t.is(await t.throwsAsync(secondPipePromise), await t.throwsAsync(secondDestination)); - t.like(await t.throwsAsync(secondDestination), {exitCode: 2}); - t.like(await t.throwsAsync(destination), {exitCode: 1}); - t.like(await t.throwsAsync(source), {exitCode: 1}); -}); - -const testDestinationTerminated = async (t, signal) => { - const source = execa('noop-repeat.js'); - const destination = execa('stdin.js'); - const pipePromise = source.pipe(destination); - destination.kill(signal); - - t.is(await t.throwsAsync(pipePromise), await t.throwsAsync(destination)); - t.like(await t.throwsAsync(destination), {signal}); - t.like(await t.throwsAsync(source), {exitCode: 1}); -}; - -test('Destination SIGTERM -> source abort', testDestinationTerminated, 'SIGTERM'); -test('Destination SIGKILL -> source abort', testDestinationTerminated, 'SIGKILL'); - -test('Source already ended -> ignore source', async t => { - const source = execa('noop.js', [foobarString]); - await source; - const destination = execa('stdin.js'); - const pipePromise = source.pipe(destination); - - t.is(await pipePromise, await destination); - t.like(await source, {stdout: foobarString}); - t.like(await destination, {stdout: ''}); -}); - -test('Source already aborted -> ignore source', async t => { - const source = execa('noop.js', [foobarString]); - source.stdout.destroy(); - const destination = execa('stdin.js'); - const pipePromise = source.pipe(destination); - - t.is(await pipePromise, await destination); - t.like(await source, {stdout: ''}); - t.like(await destination, {stdout: ''}); -}); - -test('Source already errored -> failure', async t => { - const source = execa('noop.js', [foobarString]); - const cause = new Error('test'); - source.stdout.destroy(cause); - const destination = execa('stdin.js'); - const pipePromise = source.pipe(destination); - - t.is(await t.throwsAsync(pipePromise), await t.throwsAsync(source)); - t.like(await t.throwsAsync(source), {cause}); - t.like(await destination, {stdout: ''}); -}); - -test('Destination already ended -> ignore source', async t => { - const destination = execa('stdin.js'); - destination.stdin.end('.'); - await destination; - const source = execa('noop.js', [foobarString]); - const pipePromise = source.pipe(destination); - - t.is(await pipePromise, await destination); - t.like(await destination, {stdout: '.'}); - t.like(await source, {stdout: ''}); -}); - -test('Destination already aborted -> failure', async t => { - const destination = execa('stdin.js'); - destination.stdin.destroy(); - t.like(await t.throwsAsync(destination), prematureClose); - const source = execa('noop.js', [foobarString]); - const pipePromise = source.pipe(destination); - - t.is(await t.throwsAsync(pipePromise), await t.throwsAsync(destination)); - t.like(await source, {stdout: ''}); -}); - -test('Destination already errored -> failure', async t => { - const destination = execa('stdin.js'); - const cause = new Error('test'); - destination.stdin.destroy(cause); - t.like(await t.throwsAsync(destination), {cause}); - const source = execa('noop.js', [foobarString]); - const pipePromise = source.pipe(destination); - - t.is(await t.throwsAsync(pipePromise), await t.throwsAsync(destination)); - t.like(await source, {stdout: ''}); -}); - -test('Source normal failure + destination normal failure', async t => { - const source = execa('noop-fail.js', ['1', foobarString]); - const destination = execa('stdin-fail.js'); - const pipePromise = source.pipe(destination); - - t.is(await t.throwsAsync(pipePromise), await t.throwsAsync(destination)); - t.like(await t.throwsAsync(source), {stdout: foobarString, exitCode: 2}); - t.like(await t.throwsAsync(destination), {stdout: foobarString, exitCode: 2}); -}); - -test('Simultaneous error on source and destination', async t => { - const source = execa('noop.js', ['']); - const destination = execa('stdin.js'); - const pipePromise = source.pipe(destination); - - const sourceCause = new Error(foobarString); - source.emit('error', sourceCause); - const destinationCause = new Error('other'); - destination.emit('error', destinationCause); - - t.is(await t.throwsAsync(pipePromise), await t.throwsAsync(destination)); - t.like(await t.throwsAsync(source), {cause: {originalMessage: sourceCause.originalMessage}}); - t.like(await t.throwsAsync(destination), {cause: {originalMessage: destinationCause.originalMessage}}); -}); - -test('Does not need to await individual promises', async t => { - const source = execa('fail.js'); - const destination = execa('fail.js'); - await t.throwsAsync(source.pipe(destination)); -}); - -test('Need to await .pipe() return value', async t => { - const source = execa('fail.js'); - const destination = execa('fail.js'); - const pipePromise = source.pipe(destination); - await Promise.all([ - once(process, 'unhandledRejection'), - t.throwsAsync(source), - t.throwsAsync(destination), - ]); - await t.throwsAsync(pipePromise); -}); - -if (isLinux) { - const testYesHead = async (t, useStdoutTransform, useStdinTransform, all) => { - const source = execa('yes', {stdout: useStdoutTransform ? noopGenerator(false) : 'pipe', all}); - const destination = execa('head', ['-n', '1'], {stdin: useStdinTransform ? noopGenerator(false) : 'pipe'}); - const pipePromise = source.pipe(destination); - t.is(await t.throwsAsync(pipePromise), await t.throwsAsync(source)); - t.like(await destination, {stdout: 'y'}); - t.like(await t.throwsAsync(source), {exitCode: 1, stderr: 'yes: standard output: Connection reset by peer'}); - - t.false(source.stdout.readableEnded); - t.is(source.stdout.errored, null); - t.true(source.stdout.destroyed); - t.true(source.stderr.readableEnded); - t.is(source.stderr.errored, null); - t.true(source.stderr.destroyed); - - if (all) { - t.true(source.all.readableEnded); - t.is(source.all.errored, null); - t.true(source.all.destroyed); - } - }; - - test.serial('Works with yes | head', testYesHead, false, false, false); - test.serial('Works with yes | head, input transform', testYesHead, false, true, false); - test.serial('Works with yes | head, output transform', testYesHead, true, false, false); - test.serial('Works with yes | head, input/output transform', testYesHead, true, true, false); - test.serial('Works with yes | head, "all" option', testYesHead, false, false, true); - test.serial('Works with yes | head, "all" option, input transform', testYesHead, false, true, true); - test.serial('Works with yes | head, "all" option, output transform', testYesHead, true, false, true); - test.serial('Works with yes | head, "all" option, input/output transform', testYesHead, true, true, true); -} diff --git a/test/pipe/setup.js b/test/pipe/setup.js deleted file mode 100644 index 0f683de210..0000000000 --- a/test/pipe/setup.js +++ /dev/null @@ -1,28 +0,0 @@ -import test from 'ava'; -import {execa} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {fullStdio, fullReadableStdio} from '../helpers/stdio.js'; -import {foobarString} from '../helpers/input.js'; - -setFixtureDirectory(); - -// eslint-disable-next-line max-params -const pipeToSubprocess = async (t, readableFdNumber, writableFdNumber, from, to, readableOptions = {}, writableOptions = {}) => { - const {stdout} = await execa('noop-fd.js', [`${readableFdNumber}`, foobarString], readableOptions) - .pipe(execa('stdin-fd.js', [`${writableFdNumber}`], writableOptions), {from, to}); - t.is(stdout, foobarString); -}; - -test('pipe(...) can pipe', pipeToSubprocess, 1, 0); -test('pipe(..., {from: "stdout"}) can pipe', pipeToSubprocess, 1, 0, 'stdout'); -test('pipe(..., {from: "fd1"}) can pipe', pipeToSubprocess, 1, 0, 'fd1'); -test('pipe(..., {from: "stderr"}) can pipe stderr', pipeToSubprocess, 2, 0, 'stderr'); -test('pipe(..., {from: "fd2"}) can pipe', pipeToSubprocess, 2, 0, 'fd2'); -test('pipe(..., {from: "fd3"}) can pipe', pipeToSubprocess, 3, 0, 'fd3', undefined, fullStdio); -test('pipe(..., {from: "all"}) can pipe stdout', pipeToSubprocess, 1, 0, 'all', undefined, {all: true}); -test('pipe(..., {from: "all"}) can pipe stderr', pipeToSubprocess, 2, 0, 'all', undefined, {all: true}); -test('pipe(..., {from: "all"}) can pipe stdout even with "stderr: ignore"', pipeToSubprocess, 1, 0, 'all', undefined, {all: true, stderr: 'ignore'}); -test('pipe(..., {from: "all"}) can pipe stderr even with "stdout: ignore"', pipeToSubprocess, 2, 0, 'all', undefined, {all: true, stdout: 'ignore'}); -test('pipe(..., {to: "stdin"}) can pipe', pipeToSubprocess, 1, 0, undefined, 'stdin'); -test('pipe(..., {to: "fd0"}) can pipe', pipeToSubprocess, 1, 0, undefined, 'fd0'); -test('pipe(..., {to: "fd3"}) can pipe', pipeToSubprocess, 1, 3, undefined, 'fd3', {}, fullReadableStdio()); diff --git a/test/pipe/streaming.js b/test/pipe/streaming.js deleted file mode 100644 index bd92202a90..0000000000 --- a/test/pipe/streaming.js +++ /dev/null @@ -1,440 +0,0 @@ -import {once} from 'node:events'; -import {PassThrough} from 'node:stream'; -import test from 'ava'; -import {execa} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString} from '../helpers/input.js'; -import {assertMaxListeners} from '../helpers/listeners.js'; -import {fullReadableStdio} from '../helpers/stdio.js'; -import {PARALLEL_COUNT} from '../helpers/parallel.js'; - -setFixtureDirectory(); - -test('Can pipe two sources to same destination', async t => { - const source = execa('noop.js', [foobarString]); - const secondSource = execa('noop.js', [foobarString]); - const destination = execa('stdin.js'); - const pipePromise = source.pipe(destination); - const secondPipePromise = secondSource.pipe(destination); - - t.like(await source, {stdout: foobarString}); - t.like(await secondSource, {stdout: foobarString}); - t.like(await destination, {stdout: `${foobarString}\n${foobarString}`}); - t.is(await pipePromise, await destination); - t.is(await secondPipePromise, await destination); -}); - -test('Can pipe three sources to same destination', async t => { - const source = execa('noop.js', [foobarString]); - const secondSource = execa('noop.js', [foobarString]); - const thirdSource = execa('noop.js', [foobarString]); - const destination = execa('stdin.js'); - const pipePromise = source.pipe(destination); - const secondPipePromise = secondSource.pipe(destination); - const thirdPromise = thirdSource.pipe(destination); - - t.like(await source, {stdout: foobarString}); - t.like(await secondSource, {stdout: foobarString}); - t.like(await thirdSource, {stdout: foobarString}); - t.like(await destination, {stdout: `${foobarString}\n${foobarString}\n${foobarString}`}); - t.is(await pipePromise, await destination); - t.is(await secondPipePromise, await destination); - t.is(await thirdPromise, await destination); -}); - -test.serial('Can pipe many sources to same destination', async t => { - const checkMaxListeners = assertMaxListeners(t); - - const expectedResults = Array.from({length: PARALLEL_COUNT}, (_, index) => `${index}`).sort(); - const sources = expectedResults.map(expectedResult => execa('noop.js', [expectedResult])); - const destination = execa('stdin.js'); - const pipePromises = sources.map(source => source.pipe(destination)); - - const results = await Promise.all(sources); - t.deepEqual(results.map(({stdout}) => stdout), expectedResults); - const destinationResult = await destination; - t.deepEqual(destinationResult.stdout.split('\n').sort(), expectedResults); - t.deepEqual(await Promise.all(pipePromises), sources.map(() => destinationResult)); - - checkMaxListeners(); -}); - -test.serial('Can pipe same source to many destinations', async t => { - const checkMaxListeners = assertMaxListeners(t); - - const source = execa('noop-fd.js', ['1', foobarString]); - const expectedResults = Array.from({length: PARALLEL_COUNT}, (_, index) => `${index}`); - const destinations = expectedResults.map(expectedResult => execa('noop-stdin-double.js', [expectedResult])); - const pipePromises = destinations.map(destination => source.pipe(destination)); - - t.like(await source, {stdout: foobarString}); - const results = await Promise.all(destinations); - t.deepEqual(results.map(({stdout}) => stdout), expectedResults.map(result => `${foobarString} ${result}`)); - t.deepEqual(await Promise.all(pipePromises), results); - - checkMaxListeners(); -}); - -test('Can pipe two streams from same subprocess to same destination', async t => { - const source = execa('noop-both.js', [foobarString]); - const destination = execa('stdin.js'); - const pipePromise = source.pipe(destination); - const secondPipePromise = source.pipe(destination, {from: 'stderr'}); - - t.like(await source, {stdout: foobarString, stderr: foobarString}); - t.like(await destination, {stdout: `${foobarString}\n${foobarString}`}); - t.is(await pipePromise, await destination); - t.is(await secondPipePromise, await destination); -}); - -test('Can pipe same source to two streams from same subprocess', async t => { - const source = execa('noop-fd.js', ['1', foobarString]); - const destination = execa('stdin-fd-both.js', ['3'], fullReadableStdio()); - const pipePromise = source.pipe(destination); - const secondPipePromise = source.pipe(destination, {to: 'fd3'}); - - t.like(await source, {stdout: foobarString}); - t.like(await destination, {stdout: `${foobarString}${foobarString}`}); - t.is(await pipePromise, await destination); - t.is(await secondPipePromise, await destination); -}); - -test('Can pipe a new source to same destination after some source has already written', async t => { - const passThroughStream = new PassThrough(); - const source = execa('stdin.js', {stdin: ['pipe', passThroughStream]}); - const destination = execa('stdin.js'); - const pipePromise = source.pipe(destination); - - passThroughStream.write('foo'); - const firstWrite = await once(destination.stdout, 'data'); - t.is(firstWrite.toString(), 'foo'); - - const secondSource = execa('noop.js', ['bar']); - const secondPipePromise = secondSource.pipe(destination); - passThroughStream.end(); - - t.like(await source, {stdout: 'foo'}); - t.like(await secondSource, {stdout: 'bar'}); - t.like(await destination, {stdout: 'foobar'}); - t.is(await pipePromise, await destination); - t.is(await secondPipePromise, await destination); -}); - -test('Can pipe a second source to same destination after destination has already ended', async t => { - const source = execa('noop.js', [foobarString]); - const destination = execa('stdin.js'); - const pipePromise = source.pipe(destination); - - t.like(await source, {stdout: foobarString}); - t.like(await destination, {stdout: foobarString}); - t.is(await pipePromise, await destination); - - const secondSource = execa('noop.js', [foobarString]); - const secondPipePromise = secondSource.pipe(destination); - - t.like(await secondSource, {stdout: ''}); - t.is(await secondPipePromise, await destination); -}); - -test('Can pipe same source to a second destination after source has already ended', async t => { - const source = execa('noop.js', [foobarString]); - const destination = execa('stdin.js'); - const pipePromise = source.pipe(destination); - - t.like(await source, {stdout: foobarString}); - t.like(await destination, {stdout: foobarString}); - t.is(await pipePromise, await destination); - - const secondDestination = execa('stdin.js'); - const secondPipePromise = source.pipe(secondDestination); - - t.like(await secondDestination, {stdout: ''}); - t.is(await secondPipePromise, await secondDestination); -}); - -test('Can pipe a new source to same destination after some but not all sources have ended', async t => { - const source = execa('noop.js', [foobarString]); - const passThroughStream = new PassThrough(); - const secondSource = execa('stdin.js', {stdin: ['pipe', passThroughStream]}); - const destination = execa('stdin.js'); - const pipePromise = source.pipe(destination); - const secondPipePromise = secondSource.pipe(destination); - - t.like(await source, {stdout: foobarString}); - - const thirdSource = execa('noop.js', [foobarString]); - const thirdPipePromise = thirdSource.pipe(destination); - passThroughStream.end(`${foobarString}\n`); - - t.like(await secondSource, {stdout: foobarString}); - t.like(await thirdSource, {stdout: foobarString}); - t.like(await destination, {stdout: `${foobarString}\n${foobarString}\n${foobarString}`}); - t.is(await pipePromise, await destination); - t.is(await secondPipePromise, await destination); - t.is(await thirdPipePromise, await destination); -}); - -test('Can pipe two subprocesses already ended', async t => { - const source = execa('noop.js', [foobarString]); - const destination = execa('stdin.js'); - destination.stdin.end('.'); - await Promise.all([source, destination]); - const pipePromise = source.pipe(destination); - - t.like(await source, {stdout: foobarString}); - t.like(await destination, {stdout: '.'}); - t.is(await pipePromise, await destination); -}); - -test('Can pipe to same destination through multiple paths', async t => { - const source = execa('noop.js', [foobarString]); - const destination = execa('stdin.js'); - const secondDestination = execa('stdin.js'); - const pipePromise = source.pipe(destination); - const secondPipePromise = pipePromise.pipe(secondDestination); - const thirdPipePromise = source.pipe(secondDestination); - - t.like(await source, {stdout: foobarString}); - t.like(await destination, {stdout: foobarString}); - t.like(await secondDestination, {stdout: `${foobarString}\n${foobarString}`}); - t.is(await pipePromise, await destination); - t.is(await secondPipePromise, await secondDestination); - t.is(await thirdPipePromise, await secondDestination); -}); - -test('Can pipe two sources to same destination in objectMode', async t => { - const stdoutTransform = { - * transform() { - yield [foobarString]; - }, - objectMode: true, - }; - const source = execa('noop.js', [''], {stdout: stdoutTransform}); - const secondSource = execa('noop.js', [''], {stdout: stdoutTransform}); - t.true(source.stdout.readableObjectMode); - t.true(secondSource.stdout.readableObjectMode); - - const stdinTransform = { - * transform([chunk]) { - yield chunk; - }, - objectMode: true, - }; - const destination = execa('stdin.js', {stdin: stdinTransform}); - const pipePromise = source.pipe(destination); - const secondPipePromise = secondSource.pipe(destination); - - t.like(await source, {stdout: [[foobarString]]}); - t.like(await secondSource, {stdout: [[foobarString]]}); - t.like(await destination, {stdout: `${foobarString}\n${foobarString}`}); - t.is(await pipePromise, await destination); - t.is(await secondPipePromise, await destination); -}); - -test('Can pipe one source to two destinations', async t => { - const source = execa('noop.js', [foobarString]); - const destination = execa('stdin.js'); - const secondDestination = execa('stdin.js'); - const pipePromise = source.pipe(destination); - const secondPipePromise = source.pipe(secondDestination); - - t.like(await source, {stdout: foobarString}); - t.like(await destination, {stdout: foobarString}); - t.like(await secondDestination, {stdout: foobarString}); - t.is(await pipePromise, await destination); - t.is(await secondPipePromise, await secondDestination); -}); - -test('Can pipe one source to three destinations', async t => { - const source = execa('noop.js', [foobarString]); - const destination = execa('stdin.js'); - const secondDestination = execa('stdin.js'); - const thirdDestination = execa('stdin.js'); - const pipePromise = source.pipe(destination); - const secondPipePromise = source.pipe(secondDestination); - const thirdPipePromise = source.pipe(thirdDestination); - - t.like(await source, {stdout: foobarString}); - t.like(await destination, {stdout: foobarString}); - t.like(await secondDestination, {stdout: foobarString}); - t.like(await thirdDestination, {stdout: foobarString}); - t.is(await pipePromise, await destination); - t.is(await secondPipePromise, await secondDestination); - t.is(await thirdPipePromise, await thirdDestination); -}); - -test('Can create a series of pipes', async t => { - const source = execa('noop.js', [foobarString]); - const destination = execa('stdin.js'); - const secondDestination = execa('stdin.js'); - const pipePromise = source.pipe(destination); - const secondPipePromise = pipePromise.pipe(secondDestination); - - t.like(await source, {stdout: foobarString}); - t.like(await destination, {stdout: foobarString}); - t.like(await secondDestination, {stdout: foobarString}); - t.is(await pipePromise, await destination); - t.is(await secondPipePromise, await secondDestination); -}); - -test('Returns pipedFrom on success', async t => { - const source = execa('noop.js', [foobarString]); - const destination = execa('stdin.js'); - const pipePromise = source.pipe(destination); - - const destinationResult = await destination; - t.deepEqual(destinationResult.pipedFrom, []); - const sourceResult = await source; - - t.like(await pipePromise, {pipedFrom: [sourceResult]}); - t.deepEqual(destinationResult.pipedFrom, [sourceResult]); - t.deepEqual(sourceResult.pipedFrom, []); -}); - -test('Returns pipedFrom on deep success', async t => { - const source = execa('noop.js', [foobarString]); - const destination = execa('stdin.js'); - const secondDestination = execa('stdin.js'); - const pipePromise = source.pipe(destination); - const secondPipePromise = pipePromise.pipe(secondDestination); - - const destinationResult = await destination; - t.deepEqual(destinationResult.pipedFrom, []); - const secondDestinationResult = await secondDestination; - t.deepEqual(secondDestinationResult.pipedFrom, []); - const sourceResult = await source; - - t.like(await secondPipePromise, {pipedFrom: [destinationResult]}); - t.deepEqual(secondDestinationResult.pipedFrom, [destinationResult]); - t.like(await pipePromise, {pipedFrom: [sourceResult]}); - t.deepEqual(destinationResult.pipedFrom, [sourceResult]); - t.deepEqual(sourceResult.pipedFrom, []); -}); - -test('Returns pipedFrom on source failure', async t => { - const source = execa('noop-fail.js', ['1', foobarString]); - const destination = execa('stdin.js'); - const pipePromise = source.pipe(destination); - - const destinationResult = await destination; - t.deepEqual(destinationResult.pipedFrom, []); - const sourceResult = await t.throwsAsync(source); - - t.like(await t.throwsAsync(pipePromise), {pipedFrom: []}); - t.deepEqual(destinationResult.pipedFrom, [sourceResult]); - t.deepEqual(sourceResult.pipedFrom, []); -}); - -test('Returns pipedFrom on destination failure', async t => { - const source = execa('noop.js', [foobarString]); - const destination = execa('stdin-fail.js'); - const pipePromise = source.pipe(destination); - - const destinationResult = await t.throwsAsync(destination); - const sourceResult = await source; - - t.like(await t.throwsAsync(pipePromise), {pipedFrom: [sourceResult]}); - t.deepEqual(destinationResult.pipedFrom, [sourceResult]); - t.deepEqual(sourceResult.pipedFrom, []); -}); - -test('Returns pipedFrom on source + destination failure', async t => { - const source = execa('noop-fail.js', ['1', foobarString]); - const destination = execa('stdin-fail.js'); - const pipePromise = source.pipe(destination); - - const destinationResult = await t.throwsAsync(destination); - const sourceResult = await t.throwsAsync(source); - - t.like(await t.throwsAsync(pipePromise), {pipedFrom: [sourceResult]}); - t.deepEqual(destinationResult.pipedFrom, [sourceResult]); - t.deepEqual(sourceResult.pipedFrom, []); -}); - -test('Returns pipedFrom on deep failure', async t => { - const source = execa('noop-fail.js', ['1', foobarString]); - const destination = execa('stdin-fail.js'); - const secondDestination = execa('stdin.js'); - const pipePromise = source.pipe(destination); - const secondPipePromise = pipePromise.pipe(secondDestination); - - const destinationResult = await t.throwsAsync(destination); - const secondDestinationResult = await secondDestination; - t.deepEqual(secondDestinationResult.pipedFrom, []); - const sourceResult = await t.throwsAsync(source); - - t.like(await t.throwsAsync(secondPipePromise), {pipedFrom: [sourceResult]}); - t.deepEqual(secondDestinationResult.pipedFrom, [destinationResult]); - t.like(await t.throwsAsync(pipePromise), {pipedFrom: [sourceResult]}); - t.deepEqual(destinationResult.pipedFrom, [sourceResult]); - t.deepEqual(sourceResult.pipedFrom, []); -}); - -test('Returns pipedFrom from multiple sources', async t => { - const source = execa('noop.js', [foobarString]); - const secondSource = execa('noop.js', [foobarString]); - const destination = execa('stdin.js'); - const pipePromise = source.pipe(destination); - const secondPipePromise = secondSource.pipe(destination); - - const destinationResult = await destination; - t.deepEqual(destinationResult.pipedFrom, []); - const sourceResult = await source; - const secondSourceResult = await secondSource; - - t.like(await pipePromise, {pipedFrom: [sourceResult, secondSourceResult]}); - t.like(await secondPipePromise, {pipedFrom: [sourceResult, secondSourceResult]}); - t.deepEqual(destinationResult.pipedFrom, [sourceResult, secondSourceResult]); - t.deepEqual(sourceResult.pipedFrom, []); - t.deepEqual(secondSourceResult.pipedFrom, []); -}); - -test('Returns pipedFrom from already ended subprocesses', async t => { - const source = execa('noop.js', [foobarString]); - const destination = execa('stdin.js'); - destination.stdin.end('.'); - await Promise.all([source, destination]); - const pipePromise = source.pipe(destination); - - const destinationResult = await destination; - t.deepEqual(destinationResult.pipedFrom, []); - const sourceResult = await source; - t.deepEqual(sourceResult.pipedFrom, []); - - t.like(await pipePromise, {pipedFrom: [sourceResult]}); - t.deepEqual(destinationResult.pipedFrom, [sourceResult]); - t.deepEqual(sourceResult.pipedFrom, []); -}); - -test('Does not return nor set pipedFrom on signal abort', async t => { - const abortController = new AbortController(); - const source = execa('empty.js'); - const destination = execa('empty.js'); - const pipePromise = source.pipe(destination, {unpipeSignal: abortController.signal}); - - abortController.abort(); - t.like(await t.throwsAsync(pipePromise), {pipedFrom: []}); - const destinationResult = await destination; - t.deepEqual(destinationResult.pipedFrom, []); - const sourceResult = await source; - t.deepEqual(sourceResult.pipedFrom, []); -}); - -test('Can pipe same source to same destination twice', async t => { - const source = execa('noop.js', [foobarString]); - const destination = execa('stdin.js'); - const pipePromise = source.pipe(destination); - const secondPipePromise = source.pipe(destination); - - const destinationResult = await destination; - t.like(destinationResult, {pipedFrom: []}); - const sourceResult = await source; - t.like(sourceResult, {pipedFrom: []}); - - t.like(await source, {stdout: foobarString}); - t.like(await destination, {stdout: foobarString}); - t.is(await pipePromise, destinationResult); - t.is(await secondPipePromise, destinationResult); - t.deepEqual(destinationResult.pipedFrom, [sourceResult]); - t.deepEqual(sourceResult.pipedFrom, []); -}); diff --git a/test/pipe/throw.js b/test/pipe/throw.js deleted file mode 100644 index 1b63c7cb6a..0000000000 --- a/test/pipe/throw.js +++ /dev/null @@ -1,38 +0,0 @@ -import test from 'ava'; -import {execa} from '../../index.js'; -import {foobarString} from '../helpers/input.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {assertPipeError} from '../helpers/pipe.js'; - -setFixtureDirectory(); - -test('Destination stream is ended when first argument is invalid', async t => { - const source = execa('empty.js', {stdout: 'ignore'}); - const destination = execa('stdin.js'); - const pipePromise = source.pipe(destination); - - await assertPipeError(t, pipePromise, 'option is incompatible'); - await source; - t.like(await destination, {stdout: ''}); -}); - -test('Destination stream is ended when first argument is invalid - $', async t => { - const pipePromise = execa('empty.js', {stdout: 'ignore'}).pipe`stdin.js`; - await assertPipeError(t, pipePromise, 'option is incompatible'); -}); - -test('Source stream is aborted when second argument is invalid', async t => { - const source = execa('noop.js', [foobarString]); - const pipePromise = source.pipe(false); - - await assertPipeError(t, pipePromise, 'an Execa subprocess'); - t.like(await source, {stdout: ''}); -}); - -test('Both arguments might be invalid', async t => { - const source = execa('empty.js', {stdout: 'ignore'}); - const pipePromise = source.pipe(false); - - await assertPipeError(t, pipePromise, 'an Execa subprocess'); - t.like(await source, {stdout: undefined}); -}); diff --git a/test/resolve/all.js b/test/resolve/all.js deleted file mode 100644 index 5cf7ff980b..0000000000 --- a/test/resolve/all.js +++ /dev/null @@ -1,177 +0,0 @@ -import test from 'ava'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {defaultHighWaterMark} from '../helpers/stream.js'; -import {foobarString} from '../helpers/input.js'; - -setFixtureDirectory(); - -const textEncoder = new TextEncoder(); -const foobarStringFull = `${foobarString}\n`; -const doubleFoobarStringFull = `${foobarStringFull}${foobarStringFull}`; -const doubleFoobarString = `${foobarStringFull}${foobarString}`; -const doubleFoobarUint8ArrayFull = textEncoder.encode(doubleFoobarStringFull); -const doubleFoobarUint8Array = textEncoder.encode(doubleFoobarString); -const doubleFoobarArrayFull = [foobarStringFull, foobarStringFull]; -const doubleFoobarArray = [foobarString, foobarString]; - -// eslint-disable-next-line max-params -const testAllBoth = async (t, expectedOutput, encoding, lines, stripFinalNewline, isFailure, execaMethod) => { - const fixtureName = isFailure ? 'noop-both-fail.js' : 'noop-both.js'; - const {exitCode, all} = await execaMethod(fixtureName, [foobarString], { - all: true, - encoding, - lines, - stripFinalNewline, - reject: !isFailure, - }); - t.is(exitCode, isFailure ? 1 : 0); - t.deepEqual(all, expectedOutput); -}; - -const fdOne = {stderr: true}; -const fdBoth = {stdout: true, stderr: true}; - -test('result.all is defined', testAllBoth, doubleFoobarStringFull, 'utf8', false, false, false, execa); -test('result.all is defined, encoding "buffer"', testAllBoth, doubleFoobarUint8ArrayFull, 'buffer', false, false, false, execa); -test('result.all is defined, lines', testAllBoth, doubleFoobarArrayFull, 'utf8', true, false, false, execa); -test('result.all is defined, lines, fd-specific one', testAllBoth, doubleFoobarArrayFull, 'utf8', fdOne, false, false, execa); -test('result.all is defined, lines, fd-specific both', testAllBoth, doubleFoobarArrayFull, 'utf8', fdBoth, false, false, execa); -test('result.all is defined, stripFinalNewline', testAllBoth, doubleFoobarString, 'utf8', false, true, false, execa); -test('result.all is defined, stripFinalNewline, fd-specific one', testAllBoth, doubleFoobarString, 'utf8', false, fdOne, false, execa); -test('result.all is defined, stripFinalNewline, fd-specific both', testAllBoth, doubleFoobarString, 'utf8', false, fdBoth, false, execa); -test('result.all is defined, encoding "buffer", stripFinalNewline', testAllBoth, doubleFoobarUint8Array, 'buffer', false, true, false, execa); -test('result.all is defined, encoding "buffer", stripFinalNewline, fd-specific one', testAllBoth, doubleFoobarUint8Array, 'buffer', false, fdOne, false, execa); -test('result.all is defined, encoding "buffer", stripFinalNewline, fd-specific both', testAllBoth, doubleFoobarUint8Array, 'buffer', false, fdBoth, false, execa); -test('result.all is defined, lines, stripFinalNewline', testAllBoth, doubleFoobarArray, 'utf8', true, true, false, execa); -test('result.all is defined, lines, fd-specific one, stripFinalNewline', testAllBoth, doubleFoobarArray, 'utf8', fdOne, true, false, execa); -test('result.all is defined, lines, fd-specific both, stripFinalNewline', testAllBoth, doubleFoobarArray, 'utf8', fdBoth, true, false, execa); -test('result.all is defined, lines, stripFinalNewline, fd-specific one', testAllBoth, doubleFoobarArray, 'utf8', true, fdOne, false, execa); -test('result.all is defined, lines, stripFinalNewline, fd-specific both', testAllBoth, doubleFoobarArray, 'utf8', true, fdBoth, false, execa); -test('result.all is defined, failure', testAllBoth, doubleFoobarStringFull, 'utf8', false, false, true, execa); -test('result.all is defined, encoding "buffer", failure', testAllBoth, doubleFoobarUint8ArrayFull, 'buffer', false, false, true, execa); -test('result.all is defined, lines, failure', testAllBoth, doubleFoobarArrayFull, 'utf8', true, false, true, execa); -test('result.all is defined, lines, fd-specific one, failure', testAllBoth, doubleFoobarArrayFull, 'utf8', fdOne, false, true, execa); -test('result.all is defined, lines, fd-specific both, failure', testAllBoth, doubleFoobarArrayFull, 'utf8', fdBoth, false, true, execa); -test('result.all is defined, stripFinalNewline, failure', testAllBoth, doubleFoobarString, 'utf8', false, true, true, execa); -test('result.all is defined, stripFinalNewline, fd-specific one, failure', testAllBoth, doubleFoobarString, 'utf8', false, fdOne, true, execa); -test('result.all is defined, stripFinalNewline, fd-specific both, failure', testAllBoth, doubleFoobarString, 'utf8', false, fdBoth, true, execa); -test('result.all is defined, encoding "buffer", stripFinalNewline, failure', testAllBoth, doubleFoobarUint8Array, 'buffer', false, true, true, execa); -test('result.all is defined, encoding "buffer", stripFinalNewline, fd-specific one, failure', testAllBoth, doubleFoobarUint8Array, 'buffer', false, fdOne, true, execa); -test('result.all is defined, encoding "buffer", stripFinalNewline, fd-specific both, failure', testAllBoth, doubleFoobarUint8Array, 'buffer', false, fdBoth, true, execa); -test('result.all is defined, lines, stripFinalNewline, failure', testAllBoth, doubleFoobarArray, 'utf8', true, true, true, execa); -test('result.all is defined, lines, fd-specific one, stripFinalNewline, failure', testAllBoth, doubleFoobarArray, 'utf8', fdOne, true, true, execa); -test('result.all is defined, lines, fd-specific both, stripFinalNewline, failure', testAllBoth, doubleFoobarArray, 'utf8', fdBoth, true, true, execa); -test('result.all is defined, lines, stripFinalNewline, fd-specific one, failure', testAllBoth, doubleFoobarArray, 'utf8', true, fdOne, true, execa); -test('result.all is defined, lines, stripFinalNewline, fd-specific both, failure', testAllBoth, doubleFoobarArray, 'utf8', true, fdBoth, true, execa); -test('result.all is defined, sync', testAllBoth, doubleFoobarStringFull, 'utf8', false, false, false, execaSync); -test('result.all is defined, encoding "buffer", sync', testAllBoth, doubleFoobarUint8ArrayFull, 'buffer', false, false, false, execaSync); -test('result.all is defined, lines, sync', testAllBoth, doubleFoobarArrayFull, 'utf8', true, false, false, execaSync); -test('result.all is defined, lines, fd-specific one, sync', testAllBoth, doubleFoobarArrayFull, 'utf8', fdOne, false, false, execaSync); -test('result.all is defined, lines, fd-specific both, sync', testAllBoth, doubleFoobarArrayFull, 'utf8', fdBoth, false, false, execaSync); -test('result.all is defined, stripFinalNewline, sync', testAllBoth, doubleFoobarString, 'utf8', false, true, false, execaSync); -test('result.all is defined, stripFinalNewline, fd-specific one, sync', testAllBoth, doubleFoobarString, 'utf8', false, fdOne, false, execaSync); -test('result.all is defined, stripFinalNewline, fd-specific both, sync', testAllBoth, doubleFoobarString, 'utf8', false, fdBoth, false, execaSync); -test('result.all is defined, encoding "buffer", stripFinalNewline, sync', testAllBoth, doubleFoobarUint8Array, 'buffer', false, true, false, execaSync); -test('result.all is defined, encoding "buffer", stripFinalNewline, fd-specific one, sync', testAllBoth, doubleFoobarUint8Array, 'buffer', false, fdOne, false, execaSync); -test('result.all is defined, encoding "buffer", stripFinalNewline, fd-specific both, sync', testAllBoth, doubleFoobarUint8Array, 'buffer', false, fdBoth, false, execaSync); -test('result.all is defined, lines, stripFinalNewline, sync', testAllBoth, doubleFoobarArray, 'utf8', true, true, false, execaSync); -test('result.all is defined, lines, fd-specific one, stripFinalNewline, sync', testAllBoth, doubleFoobarArray, 'utf8', fdOne, true, false, execaSync); -test('result.all is defined, lines, fd-specific both, stripFinalNewline, sync', testAllBoth, doubleFoobarArray, 'utf8', fdBoth, true, false, execaSync); -test('result.all is defined, lines, stripFinalNewline, fd-specific one, sync', testAllBoth, doubleFoobarArray, 'utf8', true, fdOne, false, execaSync); -test('result.all is defined, lines, stripFinalNewline, fd-specific both, sync', testAllBoth, doubleFoobarArray, 'utf8', true, fdBoth, false, execaSync); -test('result.all is defined, failure, sync', testAllBoth, doubleFoobarStringFull, 'utf8', false, false, true, execaSync); -test('result.all is defined, encoding "buffer", failure, sync', testAllBoth, doubleFoobarUint8ArrayFull, 'buffer', false, false, true, execaSync); -test('result.all is defined, lines, failure, sync', testAllBoth, doubleFoobarArrayFull, 'utf8', true, false, true, execaSync); -test('result.all is defined, lines, fd-specific one, failure, sync', testAllBoth, doubleFoobarArrayFull, 'utf8', fdOne, false, true, execaSync); -test('result.all is defined, lines, fd-specific both, failure, sync', testAllBoth, doubleFoobarArrayFull, 'utf8', fdBoth, false, true, execaSync); -test('result.all is defined, stripFinalNewline, failure, sync', testAllBoth, doubleFoobarString, 'utf8', false, true, true, execaSync); -test('result.all is defined, stripFinalNewline, fd-specific one, failure, sync', testAllBoth, doubleFoobarString, 'utf8', false, fdOne, true, execaSync); -test('result.all is defined, stripFinalNewline, fd-specific both, failure, sync', testAllBoth, doubleFoobarString, 'utf8', false, fdBoth, true, execaSync); -test('result.all is defined, encoding "buffer", stripFinalNewline, failure, sync', testAllBoth, doubleFoobarUint8Array, 'buffer', false, true, true, execaSync); -test('result.all is defined, encoding "buffer", stripFinalNewline, fd-specific one, failure, sync', testAllBoth, doubleFoobarUint8Array, 'buffer', false, fdOne, true, execaSync); -test('result.all is defined, encoding "buffer", stripFinalNewline, fd-specific both, failure, sync', testAllBoth, doubleFoobarUint8Array, 'buffer', false, fdBoth, true, execaSync); -test('result.all is defined, lines, stripFinalNewline, failure, sync', testAllBoth, doubleFoobarArray, 'utf8', true, true, true, execaSync); -test('result.all is defined, lines, fd-specific one, stripFinalNewline, failure, sync', testAllBoth, doubleFoobarArray, 'utf8', fdOne, true, true, execaSync); -test('result.all is defined, lines, fd-specific both, stripFinalNewline, failure, sync', testAllBoth, doubleFoobarArray, 'utf8', fdBoth, true, true, execaSync); -test('result.all is defined, lines, stripFinalNewline, fd-specific one, failure, sync', testAllBoth, doubleFoobarArray, 'utf8', true, fdOne, true, execaSync); -test('result.all is defined, lines, stripFinalNewline, fd-specific both, failure, sync', testAllBoth, doubleFoobarArray, 'utf8', true, fdBoth, true, execaSync); - -test.serial('result.all shows both `stdout` and `stderr` intermixed', async t => { - const {all} = await execa('noop-132.js', {all: true}); - t.is(all, '1\n2\n3'); -}); - -test.serial('result.all shows both `stdout` and `stderr` not intermixed, sync', t => { - const {all} = execaSync('noop-132.js', {all: true}); - t.is(all, '1\n3\n2'); -}); - -const testAllIgnored = async (t, options, execaMethod) => { - const {all} = await execaMethod('noop.js'); - t.is(all, undefined); -}; - -test('result.all is undefined unless opts.all is true', testAllIgnored, {}, execa); -test('result.all is undefined if opts.all is false', testAllIgnored, {all: false}, execa); -test('result.all is undefined if ignored', testAllIgnored, {stdio: 'ignore', all: true}, execa); -test('result.all is undefined unless opts.all is true, sync', testAllIgnored, {}, execaSync); -test('result.all is undefined if opts.all is false, sync', testAllIgnored, {all: false}, execaSync); -test('result.all is undefined if ignored, sync', testAllIgnored, {stdio: 'ignore', all: true}, execaSync); - -const testAllProperties = async (t, options) => { - const subprocess = execa('empty.js', {...options, all: true}); - t.is(subprocess.all.readableObjectMode, false); - t.is(subprocess.all.readableHighWaterMark, defaultHighWaterMark); - await subprocess; -}; - -test('subprocess.all has the right objectMode and highWaterMark - stdout + stderr', testAllProperties, {}); -test('subprocess.all has the right objectMode and highWaterMark - stdout only', testAllProperties, {stderr: 'ignore'}); -test('subprocess.all has the right objectMode and highWaterMark - stderr only', testAllProperties, {stdout: 'ignore'}); - -const testAllIgnore = async (t, streamName, otherStreamName) => { - const subprocess = execa('noop-both.js', {[otherStreamName]: 'ignore', all: true}); - t.is(subprocess[otherStreamName], null); - t.not(subprocess[streamName], null); - t.not(subprocess.all, null); - t.is(subprocess.all.readableObjectMode, subprocess[streamName].readableObjectMode); - t.is(subprocess.all.readableHighWaterMark, subprocess[streamName].readableHighWaterMark); - - const result = await subprocess; - t.is(result[otherStreamName], undefined); - t.is(result[streamName], foobarString); - t.is(result.all, foobarString); -}; - -test('can use all: true with stdout: ignore', testAllIgnore, 'stderr', 'stdout'); -test('can use all: true with stderr: ignore', testAllIgnore, 'stdout', 'stderr'); - -const testAllIgnoreSync = (t, streamName, otherStreamName) => { - const result = execaSync('noop-both.js', {[otherStreamName]: 'ignore', all: true}); - t.is(result[otherStreamName], undefined); - t.is(result[streamName], foobarString); - t.is(result.all, foobarString); -}; - -test('can use all: true with stdout: ignore, sync', testAllIgnoreSync, 'stderr', 'stdout'); -test('can use all: true with stderr: ignore, sync', testAllIgnoreSync, 'stdout', 'stderr'); - -test('can use all: true with stdout: ignore + stderr: ignore', async t => { - const subprocess = execa('noop-both.js', {stdout: 'ignore', stderr: 'ignore', all: true}); - t.is(subprocess.stdout, null); - t.is(subprocess.stderr, null); - t.is(subprocess.all, undefined); - - const {stdout, stderr, all} = await subprocess; - t.is(stdout, undefined); - t.is(stderr, undefined); - t.is(all, undefined); -}); - -test('can use all: true with stdout: ignore + stderr: ignore, sync', t => { - const {stdout, stderr, all} = execaSync('noop-both.js', {stdout: 'ignore', stderr: 'ignore', all: true}); - t.is(stdout, undefined); - t.is(stderr, undefined); - t.is(all, undefined); -}); diff --git a/test/resolve/buffer-end.js b/test/resolve/buffer-end.js deleted file mode 100644 index e6207eb7dc..0000000000 --- a/test/resolve/buffer-end.js +++ /dev/null @@ -1,78 +0,0 @@ -import {once} from 'node:events'; -import {setTimeout} from 'node:timers/promises'; -import test from 'ava'; -import {execa} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {fullStdio, getStdio} from '../helpers/stdio.js'; - -setFixtureDirectory(); - -const testBufferIgnore = async (t, fdNumber, all) => { - await t.notThrowsAsync(execa('max-buffer.js', [`${fdNumber}`], {...getStdio(fdNumber, 'ignore'), buffer: false, all})); -}; - -test('Subprocess buffers stdout, which does not prevent exit if ignored', testBufferIgnore, 1, false); -test('Subprocess buffers stderr, which does not prevent exit if ignored', testBufferIgnore, 2, false); -test('Subprocess buffers all, which does not prevent exit if ignored', testBufferIgnore, 1, true); - -const testBufferNotRead = async (t, fdNumber, all) => { - const subprocess = execa('max-buffer.js', [`${fdNumber}`], {...fullStdio, buffer: false, all}); - await t.notThrowsAsync(subprocess); -}; - -test('Subprocess buffers stdout, which does not prevent exit if not read and buffer is false', testBufferNotRead, 1, false); -test('Subprocess buffers stderr, which does not prevent exit if not read and buffer is false', testBufferNotRead, 2, false); -test('Subprocess buffers stdio[*], which does not prevent exit if not read and buffer is false', testBufferNotRead, 3, false); -test('Subprocess buffers all, which does not prevent exit if not read and buffer is false', testBufferNotRead, 1, true); - -const testBufferRead = async (t, fdNumber, all) => { - const subprocess = execa('max-buffer.js', [`${fdNumber}`], {...fullStdio, buffer: false, all}); - const stream = all ? subprocess.all : subprocess.stdio[fdNumber]; - stream.resume(); - await t.notThrowsAsync(subprocess); -}; - -test('Subprocess buffers stdout, which does not prevent exit if read and buffer is false', testBufferRead, 1, false); -test('Subprocess buffers stderr, which does not prevent exit if read and buffer is false', testBufferRead, 2, false); -test('Subprocess buffers stdio[*], which does not prevent exit if read and buffer is false', testBufferRead, 3, false); -test('Subprocess buffers all, which does not prevent exit if read and buffer is false', testBufferRead, 1, true); - -const testBufferExit = async (t, fdNumber, fixtureName, reject) => { - const subprocess = execa(fixtureName, [`${fdNumber}`], {...fullStdio, reject}); - await setTimeout(100); - const {stdio} = await subprocess; - t.is(stdio[fdNumber], 'foobar'); -}; - -test('Subprocess buffers stdout before it is read', testBufferExit, 1, 'noop-delay.js', true); -test('Subprocess buffers stderr before it is read', testBufferExit, 2, 'noop-delay.js', true); -test('Subprocess buffers stdio[*] before it is read', testBufferExit, 3, 'noop-delay.js', true); -test('Subprocess buffers stdout right away, on successfully exit', testBufferExit, 1, 'noop-fd.js', true); -test('Subprocess buffers stderr right away, on successfully exit', testBufferExit, 2, 'noop-fd.js', true); -test('Subprocess buffers stdio[*] right away, on successfully exit', testBufferExit, 3, 'noop-fd.js', true); -test('Subprocess buffers stdout right away, on failure', testBufferExit, 1, 'noop-fail.js', false); -test('Subprocess buffers stderr right away, on failure', testBufferExit, 2, 'noop-fail.js', false); -test('Subprocess buffers stdio[*] right away, on failure', testBufferExit, 3, 'noop-fail.js', false); - -const testBufferDirect = async (t, fdNumber) => { - const subprocess = execa('noop-fd.js', [`${fdNumber}`], fullStdio); - const data = await once(subprocess.stdio[fdNumber], 'data'); - t.is(data.toString().trim(), 'foobar'); - const result = await subprocess; - t.is(result.stdio[fdNumber], 'foobar'); -}; - -test('Subprocess buffers stdout right away, even if directly read', testBufferDirect, 1); -test('Subprocess buffers stderr right away, even if directly read', testBufferDirect, 2); -test('Subprocess buffers stdio[*] right away, even if directly read', testBufferDirect, 3); - -const testBufferDestroyOnEnd = async (t, fdNumber) => { - const subprocess = execa('noop-fd.js', [`${fdNumber}`], fullStdio); - const result = await subprocess; - t.is(result.stdio[fdNumber], 'foobar'); - t.true(subprocess.stdio[fdNumber].destroyed); -}; - -test('subprocess.stdout must be read right away', testBufferDestroyOnEnd, 1); -test('subprocess.stderr must be read right away', testBufferDestroyOnEnd, 2); -test('subprocess.stdio[*] must be read right away', testBufferDestroyOnEnd, 3); diff --git a/test/resolve/exit.js b/test/resolve/exit.js deleted file mode 100644 index 84c0a00ed1..0000000000 --- a/test/resolve/exit.js +++ /dev/null @@ -1,85 +0,0 @@ -import process from 'node:process'; -import test from 'ava'; -import {execa} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; - -const isWindows = process.platform === 'win32'; - -setFixtureDirectory(); - -test('exitCode is 0 on success', async t => { - const {exitCode} = await execa('noop.js', ['foo']); - t.is(exitCode, 0); -}); - -const testExitCode = async (t, expectedExitCode) => { - const {exitCode, originalMessage, shortMessage, message} = await t.throwsAsync( - execa('exit.js', [`${expectedExitCode}`]), - ); - t.is(exitCode, expectedExitCode); - t.is(originalMessage, undefined); - t.is(shortMessage, `Command failed with exit code ${expectedExitCode}: exit.js ${expectedExitCode}`); - t.is(message, shortMessage); -}; - -test('exitCode is 2', testExitCode, 2); -test('exitCode is 3', testExitCode, 3); -test('exitCode is 4', testExitCode, 4); - -if (!isWindows) { - test('error.signal is SIGINT', async t => { - const subprocess = execa('forever.js'); - - process.kill(subprocess.pid, 'SIGINT'); - - const {signal} = await t.throwsAsync(subprocess, {message: /was killed with SIGINT/}); - t.is(signal, 'SIGINT'); - }); - - test('error.signalDescription is defined', async t => { - const subprocess = execa('forever.js'); - - process.kill(subprocess.pid, 'SIGINT'); - - const {signalDescription} = await t.throwsAsync(subprocess, {message: /User interruption with CTRL-C/}); - t.is(signalDescription, 'User interruption with CTRL-C'); - }); - - test('error.signal is SIGTERM', async t => { - const subprocess = execa('forever.js'); - - process.kill(subprocess.pid, 'SIGTERM'); - - const {signal} = await t.throwsAsync(subprocess, {message: /was killed with SIGTERM/}); - t.is(signal, 'SIGTERM'); - }); - - test('error.signal uses killSignal', async t => { - const {signal} = await t.throwsAsync(execa('forever.js', {killSignal: 'SIGINT', timeout: 1, message: /timed out after/})); - t.is(signal, 'SIGINT'); - }); - - test('exitCode is undefined on signal termination', async t => { - const subprocess = execa('forever.js'); - - process.kill(subprocess.pid); - - const {exitCode} = await t.throwsAsync(subprocess); - t.is(exitCode, undefined); - }); -} - -test('result.signal is undefined for successful execution', async t => { - const {signal} = await execa('noop.js'); - t.is(signal, undefined); -}); - -test('result.signal is undefined if subprocess failed, but was not killed', async t => { - const {signal} = await t.throwsAsync(execa('fail.js')); - t.is(signal, undefined); -}); - -test('result.signalDescription is undefined for successful execution', async t => { - const {signalDescription} = await execa('noop.js'); - t.is(signalDescription, undefined); -}); diff --git a/test/resolve/no-buffer.js b/test/resolve/no-buffer.js deleted file mode 100644 index 569735d4f4..0000000000 --- a/test/resolve/no-buffer.js +++ /dev/null @@ -1,197 +0,0 @@ -import {once} from 'node:events'; -import test from 'ava'; -import getStream from 'get-stream'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {fullStdio} from '../helpers/stdio.js'; -import {foobarString, foobarUppercase, foobarUppercaseUint8Array} from '../helpers/input.js'; -import {resultGenerator, uppercaseGenerator, uppercaseBufferGenerator} from '../helpers/generator.js'; - -setFixtureDirectory(); - -const testLateStream = async (t, fdNumber, all) => { - const subprocess = execa('noop-fd-ipc.js', [`${fdNumber}`, foobarString], { - ...fullStdio, - ipc: true, - buffer: false, - all, - }); - await subprocess.getOneMessage(); - const [output, allOutput] = await Promise.all([ - getStream(subprocess.stdio[fdNumber]), - all ? getStream(subprocess.all) : undefined, - subprocess, - ]); - - t.is(output, ''); - - if (all) { - t.is(allOutput, ''); - } -}; - -test('Lacks some data when stdout is read too late `buffer` set to `false`', testLateStream, 1, false); -test('Lacks some data when stderr is read too late `buffer` set to `false`', testLateStream, 2, false); -test('Lacks some data when stdio[*] is read too late `buffer` set to `false`', testLateStream, 3, false); -test('Lacks some data when all is read too late `buffer` set to `false`', testLateStream, 1, true); - -const getFirstDataEvent = async stream => { - const [output] = await once(stream, 'data'); - return output.toString(); -}; - -// eslint-disable-next-line max-params -const testIterationBuffer = async (t, fdNumber, buffer, useDataEvents, all) => { - const subprocess = execa('noop-fd.js', [`${fdNumber}`, foobarString], {...fullStdio, buffer, all}); - const getOutput = useDataEvents ? getFirstDataEvent : getStream; - const [result, output, allOutput] = await Promise.all([ - subprocess, - getOutput(subprocess.stdio[fdNumber]), - all ? getOutput(subprocess.all) : undefined, - ]); - - const expectedResult = buffer ? foobarString : undefined; - - t.is(result.stdio[fdNumber], expectedResult); - t.is(output, foobarString); - - if (all) { - t.is(result.all, expectedResult); - t.is(allOutput, foobarString); - } -}; - -test('Can iterate stdout when `buffer` set to `false`', testIterationBuffer, 1, false, false, false); -test('Can iterate stderr when `buffer` set to `false`', testIterationBuffer, 2, false, false, false); -test('Can iterate stdio[*] when `buffer` set to `false`', testIterationBuffer, 3, false, false, false); -test('Can iterate all when `buffer` set to `false`', testIterationBuffer, 1, false, false, true); -test('Can iterate stdout when `buffer` set to `true`', testIterationBuffer, 1, true, false, false); -test('Can iterate stderr when `buffer` set to `true`', testIterationBuffer, 2, true, false, false); -test('Can iterate stdio[*] when `buffer` set to `true`', testIterationBuffer, 3, true, false, false); -test('Can iterate all when `buffer` set to `true`', testIterationBuffer, 1, true, false, true); -test('Can listen to `data` events on stdout when `buffer` set to `false`', testIterationBuffer, 1, false, true, false); -test('Can listen to `data` events on stderr when `buffer` set to `false`', testIterationBuffer, 2, false, true, false); -test('Can listen to `data` events on stdio[*] when `buffer` set to `false`', testIterationBuffer, 3, false, true, false); -test('Can listen to `data` events on all when `buffer` set to `false`', testIterationBuffer, 1, false, true, true); -test('Can listen to `data` events on stdout when `buffer` set to `true`', testIterationBuffer, 1, true, true, false); -test('Can listen to `data` events on stderr when `buffer` set to `true`', testIterationBuffer, 2, true, true, false); -test('Can listen to `data` events on stdio[*] when `buffer` set to `true`', testIterationBuffer, 3, true, true, false); -test('Can listen to `data` events on all when `buffer` set to `true`', testIterationBuffer, 1, true, true, true); - -const testNoBufferStreamError = async (t, fdNumber, all) => { - const subprocess = execa('noop-fd.js', [`${fdNumber}`], {...fullStdio, buffer: false, all}); - const stream = all ? subprocess.all : subprocess.stdio[fdNumber]; - const cause = new Error('test'); - stream.destroy(cause); - t.like(await t.throwsAsync(subprocess), {cause}); -}; - -test('Listen to stdout errors even when `buffer` is `false`', testNoBufferStreamError, 1, false); -test('Listen to stderr errors even when `buffer` is `false`', testNoBufferStreamError, 2, false); -test('Listen to stdio[*] errors even when `buffer` is `false`', testNoBufferStreamError, 3, false); -test('Listen to all errors even when `buffer` is `false`', testNoBufferStreamError, 1, true); - -const testOutput = async (t, buffer, execaMethod) => { - const {stdout} = await execaMethod('noop-fd.js', ['1', foobarString], {buffer}); - t.is(stdout, foobarString); -}; - -test('buffer: true returns output', testOutput, true, execa); -test('buffer: true returns output, fd-specific', testOutput, {stderr: false}, execa); -test('buffer: default returns output', testOutput, undefined, execa); -test('buffer: default returns output, fd-specific', testOutput, {}, execa); - -const testNoOutput = async (t, stdioOption, buffer, execaMethod) => { - const {stdout} = await execaMethod('noop.js', {stdout: stdioOption, buffer}); - t.is(stdout, undefined); -}; - -test('buffer: false does not return output', testNoOutput, 'pipe', false, execa); -test('buffer: false does not return output, fd-specific', testNoOutput, 'pipe', {stdout: false}, execa); -test('buffer: false does not return output, stdout undefined', testNoOutput, undefined, false, execa); -test('buffer: false does not return output, stdout null', testNoOutput, null, false, execa); -test('buffer: false does not return output, stdout ["pipe"]', testNoOutput, ['pipe'], false, execa); -test('buffer: false does not return output, stdout [undefined]', testNoOutput, [undefined], false, execa); -test('buffer: false does not return output, stdout [null]', testNoOutput, [null], false, execa); -test('buffer: false does not return output, stdout ["pipe", undefined]', testNoOutput, ['pipe', undefined], false, execa); -test('buffer: false does not return output, sync', testNoOutput, 'pipe', false, execaSync); -test('buffer: false does not return output, fd-specific, sync', testNoOutput, 'pipe', {stdout: false}, execaSync); -test('buffer: false does not return output, stdout undefined, sync', testNoOutput, undefined, false, execaSync); -test('buffer: false does not return output, stdout null, sync', testNoOutput, null, false, execaSync); -test('buffer: false does not return output, stdout ["pipe"], sync', testNoOutput, ['pipe'], false, execaSync); -test('buffer: false does not return output, stdout [undefined], sync', testNoOutput, [undefined], false, execaSync); -test('buffer: false does not return output, stdout [null], sync', testNoOutput, [null], false, execaSync); -test('buffer: false does not return output, stdout ["pipe", undefined], sync', testNoOutput, ['pipe', undefined], false, execaSync); - -const testNoOutputFail = async (t, execaMethod) => { - const {exitCode, stdout} = await execaMethod('fail.js', {buffer: false, reject: false}); - t.is(exitCode, 2); - t.is(stdout, undefined); -}; - -test('buffer: false does not return output, failure', testNoOutputFail, execa); -test('buffer: false does not return output, failure, sync', testNoOutputFail, execaSync); - -// eslint-disable-next-line max-params -const testNoOutputAll = async (t, buffer, bufferStdout, bufferStderr, execaMethod) => { - const {stdout, stderr, all} = await execaMethod('noop-both.js', {all: true, buffer, stripFinalNewline: false}); - t.is(stdout, bufferStdout ? `${foobarString}\n` : undefined); - t.is(stderr, bufferStderr ? `${foobarString}\n` : undefined); - const stdoutStderr = [stdout, stderr].filter(Boolean); - t.is(all, stdoutStderr.length === 0 ? undefined : stdoutStderr.join('')); -}; - -test('buffer: {}, all: true', testNoOutputAll, {}, true, true, execa); -test('buffer: {stdout: false}, all: true', testNoOutputAll, {stdout: false}, false, true, execa); -test('buffer: {stderr: false}, all: true', testNoOutputAll, {stderr: false}, true, false, execa); -test('buffer: {all: false}, all: true', testNoOutputAll, {all: false}, false, false, execa); -test('buffer: {}, all: true, sync', testNoOutputAll, {}, true, true, execaSync); -test('buffer: {stdout: false}, all: true, sync', testNoOutputAll, {stdout: false}, false, true, execaSync); -test('buffer: {stderr: false}, all: true, sync', testNoOutputAll, {stderr: false}, true, false, execaSync); -test('buffer: {all: false}, all: true, sync', testNoOutputAll, {all: false}, false, false, execaSync); - -const testTransform = async (t, objectMode, execaMethod) => { - const lines = []; - const {stdout} = await execaMethod('noop.js', { - buffer: false, - stdout: [uppercaseGenerator(objectMode), resultGenerator(lines)(objectMode)], - }); - t.is(stdout, undefined); - t.deepEqual(lines, [foobarUppercase]); -}; - -test('buffer: false still runs transforms', testTransform, false, execa); -test('buffer: false still runs transforms, objectMode', testTransform, true, execa); -test('buffer: false still runs transforms, sync', testTransform, false, execaSync); -test('buffer: false still runs transforms, objectMode, sync', testTransform, true, execaSync); - -const testTransformBinary = async (t, objectMode, execaMethod) => { - const lines = []; - const {stdout} = await execaMethod('noop-fd.js', ['1', foobarString], { - buffer: false, - stdout: [uppercaseBufferGenerator(objectMode, true), resultGenerator(lines)(objectMode)], - encoding: 'buffer', - }); - t.is(stdout, undefined); - t.deepEqual(lines, [foobarUppercaseUint8Array]); -}; - -test('buffer: false still runs transforms, encoding "buffer"', testTransformBinary, false, execa); -test('buffer: false still runs transforms, encoding "buffer", objectMode', testTransformBinary, true, execa); -test('buffer: false still runs transforms, encoding "buffer", sync', testTransformBinary, false, execaSync); -test('buffer: false still runs transforms, encoding "buffer", objectMode, sync', testTransformBinary, true, execaSync); - -const testStreamEnd = async (t, fdNumber, buffer) => { - const subprocess = execa('wrong command', {...fullStdio, buffer}); - await Promise.all([ - t.throwsAsync(subprocess, {message: /wrong command/}), - once(subprocess.stdio[fdNumber], 'end'), - ]); -}; - -test('buffer: false > emits end event on stdout when promise is rejected', testStreamEnd, 1, false); -test('buffer: false > emits end event on stderr when promise is rejected', testStreamEnd, 2, false); -test('buffer: false > emits end event on stdio[*] when promise is rejected', testStreamEnd, 3, false); -test('buffer: true > emits end event on stdout when promise is rejected', testStreamEnd, 1, true); -test('buffer: true > emits end event on stderr when promise is rejected', testStreamEnd, 2, true); -test('buffer: true > emits end event on stdio[*] when promise is rejected', testStreamEnd, 3, true); diff --git a/test/resolve/stdio.js b/test/resolve/stdio.js deleted file mode 100644 index d84fc87ad9..0000000000 --- a/test/resolve/stdio.js +++ /dev/null @@ -1,122 +0,0 @@ -import {setTimeout} from 'node:timers/promises'; -import test from 'ava'; -import {execa} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import { - fullStdio, - getStdio, - prematureClose, - assertEpipe, -} from '../helpers/stdio.js'; -import {infiniteGenerator} from '../helpers/generator.js'; - -setFixtureDirectory(); - -const getStreamInputSubprocess = fdNumber => execa('stdin-fd.js', [`${fdNumber}`], fdNumber === 3 - ? getStdio(3, [new Uint8Array(), infiniteGenerator()]) - : {}); -const getStreamOutputSubprocess = fdNumber => execa('noop-repeat.js', [`${fdNumber}`], fdNumber === 3 ? fullStdio : {}); - -const assertStreamInputError = (t, {exitCode, signal, isTerminated, failed}) => { - t.is(exitCode, 0); - t.is(signal, undefined); - t.false(isTerminated); - t.true(failed); -}; - -const assertStreamOutputError = (t, fdNumber, {exitCode, signal, isTerminated, failed, stderr}) => { - if (fdNumber !== 3) { - t.is(exitCode, 1); - } - - t.is(signal, undefined); - t.false(isTerminated); - t.true(failed); - - assertEpipe(t, stderr, fdNumber); -}; - -const testStreamInputAbort = async (t, fdNumber) => { - const subprocess = getStreamInputSubprocess(fdNumber); - subprocess.stdio[fdNumber].destroy(); - const error = await t.throwsAsync(subprocess, prematureClose); - assertStreamInputError(t, error); -}; - -test('Aborting stdin should not make the subprocess exit', testStreamInputAbort, 0); -test('Aborting input stdio[*] should not make the subprocess exit', testStreamInputAbort, 3); - -const testStreamOutputAbort = async (t, fdNumber) => { - const subprocess = getStreamOutputSubprocess(fdNumber); - subprocess.stdio[fdNumber].destroy(); - const error = await t.throwsAsync(subprocess); - assertStreamOutputError(t, fdNumber, error); -}; - -test('Aborting stdout should not make the subprocess exit', testStreamOutputAbort, 1); -test('Aborting stderr should not make the subprocess exit', testStreamOutputAbort, 2); -test('Aborting output stdio[*] should not make the subprocess exit', testStreamOutputAbort, 3); - -const testStreamInputDestroy = async (t, fdNumber) => { - const subprocess = getStreamInputSubprocess(fdNumber); - const cause = new Error('test'); - subprocess.stdio[fdNumber].destroy(cause); - const error = await t.throwsAsync(subprocess); - t.is(error.cause, cause); - assertStreamInputError(t, error); -}; - -test('Destroying stdin should not make the subprocess exit', testStreamInputDestroy, 0); -test('Destroying input stdio[*] should not make the subprocess exit', testStreamInputDestroy, 3); - -const testStreamOutputDestroy = async (t, fdNumber) => { - const subprocess = getStreamOutputSubprocess(fdNumber); - const cause = new Error('test'); - subprocess.stdio[fdNumber].destroy(cause); - const error = await t.throwsAsync(subprocess); - t.is(error.cause, cause); - assertStreamOutputError(t, fdNumber, error); -}; - -test('Destroying stdout should not make the subprocess exit', testStreamOutputDestroy, 1); -test('Destroying stderr should not make the subprocess exit', testStreamOutputDestroy, 2); -test('Destroying output stdio[*] should not make the subprocess exit', testStreamOutputDestroy, 3); - -const testStreamInputError = async (t, fdNumber) => { - const subprocess = getStreamInputSubprocess(fdNumber); - const cause = new Error('test'); - const stream = subprocess.stdio[fdNumber]; - stream.emit('error', cause); - stream.end(); - const error = await t.throwsAsync(subprocess); - t.is(error.cause, cause); - assertStreamInputError(t, error); -}; - -test('Errors on stdin should not make the subprocess exit', testStreamInputError, 0); -test('Errors on input stdio[*] should not make the subprocess exit', testStreamInputError, 3); - -const testStreamOutputError = async (t, fdNumber) => { - const subprocess = getStreamOutputSubprocess(fdNumber); - const cause = new Error('test'); - const stream = subprocess.stdio[fdNumber]; - stream.emit('error', cause); - const error = await t.throwsAsync(subprocess); - t.is(error.cause, cause); - assertStreamOutputError(t, fdNumber, error); -}; - -test('Errors on stdout should make the subprocess exit', testStreamOutputError, 1); -test('Errors on stderr should make the subprocess exit', testStreamOutputError, 2); -test('Errors on output stdio[*] should make the subprocess exit', testStreamOutputError, 3); - -const testWaitOnStreamEnd = async (t, fdNumber) => { - const subprocess = execa('stdin-fd.js', [`${fdNumber}`], fullStdio); - await setTimeout(100); - subprocess.stdio[fdNumber].end('foobar'); - const {stdout} = await subprocess; - t.is(stdout, 'foobar'); -}; - -test('Subprocess waits on stdin before exiting', testWaitOnStreamEnd, 0); -test('Subprocess waits on stdio[*] before exiting', testWaitOnStreamEnd, 3); diff --git a/test/resolve/wait-abort.js b/test/resolve/wait-abort.js deleted file mode 100644 index 411d397c1f..0000000000 --- a/test/resolve/wait-abort.js +++ /dev/null @@ -1,104 +0,0 @@ -import {setImmediate} from 'node:timers/promises'; -import test from 'ava'; -import {execa} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {prematureClose} from '../helpers/stdio.js'; -import {noopReadable, noopWritable, noopDuplex} from '../helpers/stream.js'; -import { - endOptionStream, - destroyOptionStream, - destroySubprocessStream, - getStreamStdio, -} from '../helpers/wait.js'; - -setFixtureDirectory(); - -const noop = () => {}; - -// eslint-disable-next-line max-params -const testStreamAbortWait = async (t, streamMethod, stream, fdNumber, useTransform) => { - const subprocess = execa('noop-stdin-fd.js', [`${fdNumber}`], getStreamStdio(fdNumber, stream, useTransform)); - streamMethod({stream, subprocess, fdNumber}); - subprocess.stdin.end(); - await setImmediate(); - stream.destroy(); - - const {stdout} = await subprocess; - t.is(stdout, ''); - t.true(stream.destroyed); -}; - -test('Keeps running when stdin option is used and subprocess.stdin ends', testStreamAbortWait, noop, noopReadable(), 0, false); -test('Keeps running when stdin option is used and subprocess.stdin Duplex ends', testStreamAbortWait, noop, noopDuplex(), 0, false); -test('Keeps running when input stdio[*] option is used and input subprocess.stdio[*] ends', testStreamAbortWait, noop, noopReadable(), 3, false); -test('Keeps running when stdin option is used and subprocess.stdin ends, with a transform', testStreamAbortWait, noop, noopReadable(), 0, true); -test('Keeps running when stdin option is used and subprocess.stdin Duplex ends, with a transform', testStreamAbortWait, noop, noopDuplex(), 0, true); -test('Keeps running when input stdio[*] option is used and input subprocess.stdio[*] ends, with a transform', testStreamAbortWait, noop, noopReadable(), 3, true); - -// eslint-disable-next-line max-params -const testStreamAbortSuccess = async (t, streamMethod, stream, fdNumber, useTransform) => { - const subprocess = execa('noop-stdin-fd.js', [`${fdNumber}`], getStreamStdio(fdNumber, stream, useTransform)); - streamMethod({stream, subprocess, fdNumber}); - subprocess.stdin.end(); - - const {stdout} = await subprocess; - t.is(stdout, ''); - t.true(stream.destroyed); -}; - -test('Passes when stdin option aborts', testStreamAbortSuccess, destroyOptionStream, noopReadable(), 0, false); -test('Passes when stdin option Duplex aborts', testStreamAbortSuccess, destroyOptionStream, noopDuplex(), 0, false); -test('Passes when input stdio[*] option aborts', testStreamAbortSuccess, destroyOptionStream, noopReadable(), 3, false); -test('Passes when stdout option ends with no more writes', testStreamAbortSuccess, endOptionStream, noopWritable(), 1, false); -test('Passes when stderr option ends with no more writes', testStreamAbortSuccess, endOptionStream, noopWritable(), 2, false); -test('Passes when output stdio[*] option ends with no more writes', testStreamAbortSuccess, endOptionStream, noopWritable(), 3, false); -test('Passes when subprocess.stdout aborts with no more writes', testStreamAbortSuccess, destroySubprocessStream, noopWritable(), 1, false); -test('Passes when subprocess.stdout Duplex aborts with no more writes', testStreamAbortSuccess, destroySubprocessStream, noopDuplex(), 1, false); -test('Passes when subprocess.stderr aborts with no more writes', testStreamAbortSuccess, destroySubprocessStream, noopWritable(), 2, false); -test('Passes when subprocess.stderr Duplex aborts with no more writes', testStreamAbortSuccess, destroySubprocessStream, noopDuplex(), 2, false); -test('Passes when output subprocess.stdio[*] aborts with no more writes', testStreamAbortSuccess, destroySubprocessStream, noopWritable(), 3, false); -test('Passes when output subprocess.stdio[*] Duplex aborts with no more writes', testStreamAbortSuccess, destroySubprocessStream, noopDuplex(), 3, false); -test('Passes when stdin option aborts, with a transform', testStreamAbortSuccess, destroyOptionStream, noopReadable(), 0, true); -test('Passes when stdin option Duplex aborts, with a transform', testStreamAbortSuccess, destroyOptionStream, noopDuplex(), 0, true); -test('Passes when input stdio[*] option aborts, with a transform', testStreamAbortSuccess, destroyOptionStream, noopReadable(), 3, true); -test('Passes when stdout option ends with no more writes, with a transform', testStreamAbortSuccess, endOptionStream, noopWritable(), 1, true); -test('Passes when stderr option ends with no more writes, with a transform', testStreamAbortSuccess, endOptionStream, noopWritable(), 2, true); -test('Passes when output stdio[*] option ends with no more writes, with a transform', testStreamAbortSuccess, endOptionStream, noopWritable(), 3, true); -test('Passes when subprocess.stdout aborts with no more writes, with a transform', testStreamAbortSuccess, destroySubprocessStream, noopWritable(), 1, true); -test('Passes when subprocess.stdout Duplex aborts with no more writes, with a transform', testStreamAbortSuccess, destroySubprocessStream, noopDuplex(), 1, true); -test('Passes when subprocess.stderr aborts with no more writes, with a transform', testStreamAbortSuccess, destroySubprocessStream, noopWritable(), 2, true); -test('Passes when subprocess.stderr Duplex aborts with no more writes, with a transform', testStreamAbortSuccess, destroySubprocessStream, noopDuplex(), 2, true); -test('Passes when output subprocess.stdio[*] aborts with no more writes, with a transform', testStreamAbortSuccess, destroySubprocessStream, noopWritable(), 3, true); -test('Passes when output subprocess.stdio[*] Duplex aborts with no more writes, with a transform', testStreamAbortSuccess, destroySubprocessStream, noopDuplex(), 3, true); - -// eslint-disable-next-line max-params -const testStreamAbortFail = async (t, streamMethod, stream, fdNumber, useTransform) => { - const subprocess = execa('noop-stdin-fd.js', [`${fdNumber}`], getStreamStdio(fdNumber, stream, useTransform)); - streamMethod({stream, subprocess, fdNumber}); - if (fdNumber !== 0) { - subprocess.stdin.end(); - } - - const error = await t.throwsAsync(subprocess); - t.like(error, {...prematureClose, exitCode: 0}); - t.true(stream.destroyed); -}; - -test('Throws abort error when subprocess.stdin aborts', testStreamAbortFail, destroySubprocessStream, noopReadable(), 0, false); -test('Throws abort error when subprocess.stdin Duplex aborts', testStreamAbortFail, destroySubprocessStream, noopDuplex(), 0, false); -test('Throws abort error when input subprocess.stdio[*] aborts', testStreamAbortFail, destroySubprocessStream, noopReadable(), 3, false); -test('Throws abort error when stdout option aborts with no more writes', testStreamAbortFail, destroyOptionStream, noopWritable(), 1, false); -test('Throws abort error when stdout option Duplex aborts with no more writes', testStreamAbortFail, destroyOptionStream, noopDuplex(), 1, false); -test('Throws abort error when stderr option aborts with no more writes', testStreamAbortFail, destroyOptionStream, noopWritable(), 2, false); -test('Throws abort error when stderr option Duplex aborts with no more writes', testStreamAbortFail, destroyOptionStream, noopDuplex(), 2, false); -test('Throws abort error when output stdio[*] option aborts with no more writes', testStreamAbortFail, destroyOptionStream, noopWritable(), 3, false); -test('Throws abort error when output stdio[*] Duplex option aborts with no more writes', testStreamAbortFail, destroyOptionStream, noopDuplex(), 3, false); -test('Throws abort error when subprocess.stdin aborts, with a transform', testStreamAbortFail, destroySubprocessStream, noopReadable(), 0, true); -test('Throws abort error when subprocess.stdin Duplex aborts, with a transform', testStreamAbortFail, destroySubprocessStream, noopDuplex(), 0, true); -test('Throws abort error when input subprocess.stdio[*] aborts, with a transform', testStreamAbortFail, destroySubprocessStream, noopReadable(), 3, true); -test('Throws abort error when stdout option aborts with no more writes, with a transform', testStreamAbortFail, destroyOptionStream, noopWritable(), 1, true); -test('Throws abort error when stdout option Duplex aborts with no more writes, with a transform', testStreamAbortFail, destroyOptionStream, noopDuplex(), 1, true); -test('Throws abort error when stderr option aborts with no more writes, with a transform', testStreamAbortFail, destroyOptionStream, noopWritable(), 2, true); -test('Throws abort error when stderr option Duplex aborts with no more writes, with a transform', testStreamAbortFail, destroyOptionStream, noopDuplex(), 2, true); -test('Throws abort error when output stdio[*] option aborts with no more writes, with a transform', testStreamAbortFail, destroyOptionStream, noopWritable(), 3, true); -test('Throws abort error when output stdio[*] Duplex option aborts with no more writes, with a transform', testStreamAbortFail, destroyOptionStream, noopDuplex(), 3, true); diff --git a/test/resolve/wait-epipe.js b/test/resolve/wait-epipe.js deleted file mode 100644 index 5f66af629a..0000000000 --- a/test/resolve/wait-epipe.js +++ /dev/null @@ -1,60 +0,0 @@ -import {setImmediate} from 'node:timers/promises'; -import test from 'ava'; -import {execa} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {assertEpipe} from '../helpers/stdio.js'; -import {foobarString} from '../helpers/input.js'; -import {noopWritable, noopDuplex} from '../helpers/stream.js'; -import { - endOptionStream, - destroyOptionStream, - destroySubprocessStream, - getStreamStdio, -} from '../helpers/wait.js'; - -setFixtureDirectory(); - -// eslint-disable-next-line max-params -const testStreamEpipeFail = async (t, streamMethod, stream, fdNumber, useTransform) => { - const subprocess = execa('noop-stdin-fd.js', [`${fdNumber}`], getStreamStdio(fdNumber, stream, useTransform)); - streamMethod({stream, subprocess, fdNumber}); - await setImmediate(); - subprocess.stdin.end(foobarString); - - const {exitCode, stdio, stderr} = await t.throwsAsync(subprocess); - t.is(exitCode, 1); - t.is(stdio[fdNumber], ''); - t.true(stream.destroyed); - assertEpipe(t, stderr, fdNumber); -}; - -test('Throws EPIPE when stdout option ends with more writes', testStreamEpipeFail, endOptionStream, noopWritable(), 1, false); -test('Throws EPIPE when stdout option aborts with more writes', testStreamEpipeFail, destroyOptionStream, noopWritable(), 1, false); -test('Throws EPIPE when stdout option Duplex aborts with more writes', testStreamEpipeFail, destroyOptionStream, noopDuplex(), 1, false); -test('Throws EPIPE when stderr option ends with more writes', testStreamEpipeFail, endOptionStream, noopWritable(), 2, false); -test('Throws EPIPE when stderr option aborts with more writes', testStreamEpipeFail, destroyOptionStream, noopWritable(), 2, false); -test('Throws EPIPE when stderr option Duplex aborts with more writes', testStreamEpipeFail, destroyOptionStream, noopDuplex(), 2, false); -test('Throws EPIPE when output stdio[*] option ends with more writes', testStreamEpipeFail, endOptionStream, noopWritable(), 3, false); -test('Throws EPIPE when output stdio[*] option aborts with more writes', testStreamEpipeFail, destroyOptionStream, noopWritable(), 3, false); -test('Throws EPIPE when output stdio[*] option Duplex aborts with more writes', testStreamEpipeFail, destroyOptionStream, noopDuplex(), 3, false); -test('Throws EPIPE when subprocess.stdout aborts with more writes', testStreamEpipeFail, destroySubprocessStream, noopWritable(), 1, false); -test('Throws EPIPE when subprocess.stdout Duplex aborts with more writes', testStreamEpipeFail, destroySubprocessStream, noopDuplex(), 1, false); -test('Throws EPIPE when subprocess.stderr aborts with more writes', testStreamEpipeFail, destroySubprocessStream, noopWritable(), 2, false); -test('Throws EPIPE when subprocess.stderr Duplex aborts with more writes', testStreamEpipeFail, destroySubprocessStream, noopDuplex(), 2, false); -test('Throws EPIPE when output subprocess.stdio[*] aborts with more writes', testStreamEpipeFail, destroySubprocessStream, noopWritable(), 3, false); -test('Throws EPIPE when output subprocess.stdio[*] Duplex aborts with more writes', testStreamEpipeFail, destroySubprocessStream, noopDuplex(), 3, false); -test('Throws EPIPE when stdout option ends with more writes, with a transform', testStreamEpipeFail, endOptionStream, noopWritable(), 1, true); -test('Throws EPIPE when stdout option aborts with more writes, with a transform', testStreamEpipeFail, destroyOptionStream, noopWritable(), 1, true); -test('Throws EPIPE when stdout option Duplex aborts with more writes, with a transform', testStreamEpipeFail, destroyOptionStream, noopDuplex(), 1, true); -test('Throws EPIPE when stderr option ends with more writes, with a transform', testStreamEpipeFail, endOptionStream, noopWritable(), 2, true); -test('Throws EPIPE when stderr option aborts with more writes, with a transform', testStreamEpipeFail, destroyOptionStream, noopWritable(), 2, true); -test('Throws EPIPE when stderr option Duplex aborts with more writes, with a transform', testStreamEpipeFail, destroyOptionStream, noopDuplex(), 2, true); -test('Throws EPIPE when output stdio[*] option ends with more writes, with a transform', testStreamEpipeFail, endOptionStream, noopWritable(), 3, true); -test('Throws EPIPE when output stdio[*] option aborts with more writes, with a transform', testStreamEpipeFail, destroyOptionStream, noopWritable(), 3, true); -test('Throws EPIPE when output stdio[*] option Duplex aborts with more writes, with a transform', testStreamEpipeFail, destroyOptionStream, noopDuplex(), 3, true); -test('Throws EPIPE when subprocess.stdout aborts with more writes, with a transform', testStreamEpipeFail, destroySubprocessStream, noopWritable(), 1, true); -test('Throws EPIPE when subprocess.stdout Duplex aborts with more writes, with a transform', testStreamEpipeFail, destroySubprocessStream, noopDuplex(), 1, true); -test('Throws EPIPE when subprocess.stderr aborts with more writes, with a transform', testStreamEpipeFail, destroySubprocessStream, noopWritable(), 2, true); -test('Throws EPIPE when subprocess.stderr Duplex aborts with more writes, with a transform', testStreamEpipeFail, destroySubprocessStream, noopDuplex(), 2, true); -test('Throws EPIPE when output subprocess.stdio[*] aborts with more writes, with a transform', testStreamEpipeFail, destroySubprocessStream, noopWritable(), 3, true); -test('Throws EPIPE when output subprocess.stdio[*] Duplex aborts with more writes, with a transform', testStreamEpipeFail, destroySubprocessStream, noopDuplex(), 3, true); diff --git a/test/resolve/wait-error.js b/test/resolve/wait-error.js deleted file mode 100644 index ac29f63843..0000000000 --- a/test/resolve/wait-error.js +++ /dev/null @@ -1,64 +0,0 @@ -import test from 'ava'; -import {execa} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {noopReadable, noopWritable, noopDuplex} from '../helpers/stream.js'; -import {destroyOptionStream, destroySubprocessStream, getStreamStdio} from '../helpers/wait.js'; - -setFixtureDirectory(); - -// eslint-disable-next-line max-params -const testStreamError = async (t, streamMethod, stream, fdNumber, useTransform) => { - const subprocess = execa('empty.js', getStreamStdio(fdNumber, stream, useTransform)); - const cause = new Error('test'); - streamMethod({ - stream, - subprocess, - fdNumber, - error: cause, - }); - - const error = await t.throwsAsync(subprocess); - t.is(error.cause, cause); - t.is(error.exitCode, 0); - t.is(error.signal, undefined); - t.false(error.isTerminated); - t.true(error.failed); - t.true(stream.destroyed); -}; - -test('Throws stream error when stdin option errors', testStreamError, destroyOptionStream, noopReadable(), 0, false); -test('Throws stream error when stdin option Duplex errors', testStreamError, destroyOptionStream, noopDuplex(), 0, false); -test('Throws stream error when stdout option errors', testStreamError, destroyOptionStream, noopWritable(), 1, false); -test('Throws stream error when stdout option Duplex errors', testStreamError, destroyOptionStream, noopDuplex(), 1, false); -test('Throws stream error when stderr option errors', testStreamError, destroyOptionStream, noopWritable(), 2, false); -test('Throws stream error when stderr option Duplex errors', testStreamError, destroyOptionStream, noopDuplex(), 2, false); -test('Throws stream error when output stdio[*] option errors', testStreamError, destroyOptionStream, noopWritable(), 3, false); -test('Throws stream error when output stdio[*] Duplex option errors', testStreamError, destroyOptionStream, noopDuplex(), 3, false); -test('Throws stream error when input stdio[*] option errors', testStreamError, destroyOptionStream, noopReadable(), 3, false); -test('Throws stream error when subprocess.stdin errors', testStreamError, destroySubprocessStream, noopReadable(), 0, false); -test('Throws stream error when subprocess.stdin Duplex errors', testStreamError, destroySubprocessStream, noopDuplex(), 0, false); -test('Throws stream error when subprocess.stdout errors', testStreamError, destroySubprocessStream, noopWritable(), 1, false); -test('Throws stream error when subprocess.stdout Duplex errors', testStreamError, destroySubprocessStream, noopDuplex(), 1, false); -test('Throws stream error when subprocess.stderr errors', testStreamError, destroySubprocessStream, noopWritable(), 2, false); -test('Throws stream error when subprocess.stderr Duplex errors', testStreamError, destroySubprocessStream, noopDuplex(), 2, false); -test('Throws stream error when output subprocess.stdio[*] errors', testStreamError, destroySubprocessStream, noopWritable(), 3, false); -test('Throws stream error when output subprocess.stdio[*] Duplex errors', testStreamError, destroySubprocessStream, noopDuplex(), 3, false); -test('Throws stream error when input subprocess.stdio[*] errors', testStreamError, destroySubprocessStream, noopReadable(), 3, false); -test('Throws stream error when stdin option errors, with a transform', testStreamError, destroyOptionStream, noopReadable(), 0, true); -test('Throws stream error when stdin option Duplex errors, with a transform', testStreamError, destroyOptionStream, noopDuplex(), 0, true); -test('Throws stream error when stdout option errors, with a transform', testStreamError, destroyOptionStream, noopWritable(), 1, true); -test('Throws stream error when stdout option Duplex errors, with a transform', testStreamError, destroyOptionStream, noopDuplex(), 1, true); -test('Throws stream error when stderr option errors, with a transform', testStreamError, destroyOptionStream, noopWritable(), 2, true); -test('Throws stream error when stderr option Duplex errors, with a transform', testStreamError, destroyOptionStream, noopDuplex(), 2, true); -test('Throws stream error when output stdio[*] option errors, with a transform', testStreamError, destroyOptionStream, noopWritable(), 3, true); -test('Throws stream error when output stdio[*] Duplex option errors, with a transform', testStreamError, destroyOptionStream, noopDuplex(), 3, true); -test('Throws stream error when input stdio[*] option errors, with a transform', testStreamError, destroyOptionStream, noopReadable(), 3, true); -test('Throws stream error when subprocess.stdin errors, with a transform', testStreamError, destroySubprocessStream, noopReadable(), 0, true); -test('Throws stream error when subprocess.stdin Duplex errors, with a transform', testStreamError, destroySubprocessStream, noopDuplex(), 0, true); -test('Throws stream error when subprocess.stdout errors, with a transform', testStreamError, destroySubprocessStream, noopWritable(), 1, true); -test('Throws stream error when subprocess.stdout Duplex errors, with a transform', testStreamError, destroySubprocessStream, noopDuplex(), 1, true); -test('Throws stream error when subprocess.stderr errors, with a transform', testStreamError, destroySubprocessStream, noopWritable(), 2, true); -test('Throws stream error when subprocess.stderr Duplex errors, with a transform', testStreamError, destroySubprocessStream, noopDuplex(), 2, true); -test('Throws stream error when output subprocess.stdio[*] errors, with a transform', testStreamError, destroySubprocessStream, noopWritable(), 3, true); -test('Throws stream error when output subprocess.stdio[*] Duplex errors, with a transform', testStreamError, destroySubprocessStream, noopDuplex(), 3, true); -test('Throws stream error when input subprocess.stdio[*] errors, with a transform', testStreamError, destroySubprocessStream, noopReadable(), 3, true); diff --git a/test/resolve/wait-subprocess.js b/test/resolve/wait-subprocess.js deleted file mode 100644 index 7d4a8b3e17..0000000000 --- a/test/resolve/wait-subprocess.js +++ /dev/null @@ -1,49 +0,0 @@ -import test from 'ava'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {getStdio} from '../helpers/stdio.js'; - -setFixtureDirectory(); - -const testIgnore = async (t, fdNumber, execaMethod) => { - const result = await execaMethod('noop.js', getStdio(fdNumber, 'ignore')); - t.is(result.stdio[fdNumber], undefined); -}; - -test('stdout is undefined if ignored', testIgnore, 1, execa); -test('stderr is undefined if ignored', testIgnore, 2, execa); -test('stdio[*] is undefined if ignored', testIgnore, 3, execa); -test('stdout is undefined if ignored - sync', testIgnore, 1, execaSync); -test('stderr is undefined if ignored - sync', testIgnore, 2, execaSync); -test('stdio[*] is undefined if ignored - sync', testIgnore, 3, execaSync); - -const testSubprocessEventsCleanup = async (t, fixtureName) => { - const subprocess = execa(fixtureName, {reject: false}); - t.deepEqual(subprocess.eventNames().map(String).sort(), ['error', 'exit', 'spawn']); - await subprocess; - t.deepEqual(subprocess.eventNames(), []); -}; - -test('subprocess listeners are cleaned up on success', testSubprocessEventsCleanup, 'empty.js'); -test('subprocess listeners are cleaned up on failure', testSubprocessEventsCleanup, 'fail.js'); - -test('Aborting stdout should not abort stderr nor all', async t => { - const subprocess = execa('empty.js', {all: true}); - - subprocess.stdout.destroy(); - t.false(subprocess.stdout.readable); - t.true(subprocess.stderr.readable); - t.true(subprocess.all.readable); - - await subprocess; - - t.false(subprocess.stdout.readableEnded); - t.is(subprocess.stdout.errored, null); - t.true(subprocess.stdout.destroyed); - t.true(subprocess.stderr.readableEnded); - t.is(subprocess.stderr.errored, null); - t.true(subprocess.stderr.destroyed); - t.true(subprocess.all.readableEnded); - t.is(subprocess.all.errored, null); - t.true(subprocess.all.destroyed); -}); diff --git a/test/return/duration.js b/test/return/duration.js deleted file mode 100644 index 638ac61988..0000000000 --- a/test/return/duration.js +++ /dev/null @@ -1,61 +0,0 @@ -import test from 'ava'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {getEarlyErrorSubprocess, getEarlyErrorSubprocessSync} from '../helpers/early-error.js'; - -setFixtureDirectory(); - -const assertDurationMs = (t, durationMs) => { - t.is(typeof durationMs, 'number'); - t.true(Number.isFinite(durationMs)); - t.not(durationMs, 0); - t.true(durationMs > 0); -}; - -test('result.durationMs', async t => { - const {durationMs} = await execa('empty.js'); - assertDurationMs(t, durationMs); -}); - -test('result.durationMs - sync', t => { - const {durationMs} = execaSync('empty.js'); - assertDurationMs(t, durationMs); -}); - -test('error.durationMs', async t => { - const {durationMs} = await t.throwsAsync(execa('fail.js')); - assertDurationMs(t, durationMs); -}); - -test('error.durationMs - sync', t => { - const {durationMs} = t.throws(() => { - execaSync('fail.js'); - }); - assertDurationMs(t, durationMs); -}); - -test('error.durationMs - early validation', async t => { - const {durationMs} = await t.throwsAsync(getEarlyErrorSubprocess()); - assertDurationMs(t, durationMs); -}); - -test('error.durationMs - early validation, sync', t => { - const {durationMs} = t.throws(getEarlyErrorSubprocessSync); - assertDurationMs(t, durationMs); -}); - -test('error.durationMs - unpipeSignal', async t => { - const {durationMs} = await t.throwsAsync(execa('noop.js').pipe('stdin.js', {signal: AbortSignal.abort()})); - assertDurationMs(t, durationMs); -}); - -test('error.durationMs - pipe validation', async t => { - const {durationMs} = await t.throwsAsync(execa('noop.js').pipe(false)); - assertDurationMs(t, durationMs); -}); - -test.serial('result.durationMs is accurate', async t => { - const minDurationMs = 1e3; - const {durationMs} = await execa('delay.js', [minDurationMs]); - t.true(durationMs >= minDurationMs); -}); diff --git a/test/return/early-error.js b/test/return/early-error.js deleted file mode 100644 index bdfa8a1ee4..0000000000 --- a/test/return/early-error.js +++ /dev/null @@ -1,115 +0,0 @@ -import process from 'node:process'; -import test from 'ava'; -import {execa, execaSync, $} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {fullStdio} from '../helpers/stdio.js'; -import { - earlyErrorOptions, - getEarlyErrorSubprocess, - getEarlyErrorSubprocessSync, - expectedEarlyError, - expectedEarlyErrorSync, -} from '../helpers/early-error.js'; - -setFixtureDirectory(); - -const isWindows = process.platform === 'win32'; -const ENOENT_REGEXP = isWindows ? /failed with exit code 1/ : /spawn.* ENOENT/; - -test('execaSync() throws error if ENOENT', t => { - t.throws(() => { - execaSync('foo'); - }, {message: ENOENT_REGEXP}); -}); - -const testEarlyErrorShape = async (t, reject) => { - const subprocess = getEarlyErrorSubprocess({reject}); - t.notThrows(() => { - // eslint-disable-next-line promise/prefer-await-to-then - subprocess.catch(() => {}); - subprocess.unref(); - subprocess.on('error', () => {}); - }); -}; - -test('child_process.spawn() early errors have correct shape', testEarlyErrorShape, true); -test('child_process.spawn() early errors have correct shape - reject false', testEarlyErrorShape, false); - -test('child_process.spawn() early errors are propagated', async t => { - await t.throwsAsync(getEarlyErrorSubprocess(), expectedEarlyError); -}); - -test('child_process.spawn() early errors are returned', async t => { - const {failed} = await getEarlyErrorSubprocess({reject: false}); - t.true(failed); -}); - -test('child_process.spawnSync() early errors are propagated with a correct shape', t => { - t.throws(getEarlyErrorSubprocessSync, expectedEarlyErrorSync); -}); - -test('child_process.spawnSync() early errors are propagated with a correct shape - reject false', t => { - const {failed} = getEarlyErrorSubprocessSync({reject: false}); - t.true(failed); -}); - -if (!isWindows) { - test('execa() rejects if running non-executable', async t => { - await t.throwsAsync(execa('non-executable.js')); - }); - - test('execa() rejects with correct error and doesn\'t throw if running non-executable with input', async t => { - await t.throwsAsync(execa('non-executable.js', {input: 'Hey!'}), {message: /EACCES/}); - }); - - test('write to fast-exit subprocess', async t => { - // Try-catch here is necessary, because this test is not 100% accurate - // Sometimes subprocess can manage to accept input before exiting - try { - await execa(`fast-exit-${process.platform}`, [], {input: 'data'}); - t.pass(); - } catch (error) { - t.is(error.code, 'EPIPE'); - } - }); -} - -const testEarlyErrorPipe = async (t, getSubprocess) => { - await t.throwsAsync(getSubprocess(), expectedEarlyError); -}; - -test('child_process.spawn() early errors on source can use .pipe()', testEarlyErrorPipe, () => getEarlyErrorSubprocess().pipe(execa('empty.js'))); -test('child_process.spawn() early errors on destination can use .pipe()', testEarlyErrorPipe, () => execa('empty.js').pipe(getEarlyErrorSubprocess())); -test('child_process.spawn() early errors on source and destination can use .pipe()', testEarlyErrorPipe, () => getEarlyErrorSubprocess().pipe(getEarlyErrorSubprocess())); -test('child_process.spawn() early errors can use .pipe() multiple times', testEarlyErrorPipe, () => getEarlyErrorSubprocess().pipe(getEarlyErrorSubprocess()).pipe(getEarlyErrorSubprocess())); -test('child_process.spawn() early errors can use .pipe``', testEarlyErrorPipe, () => $(earlyErrorOptions)`empty.js`.pipe(earlyErrorOptions)`empty.js`); -test('child_process.spawn() early errors can use .pipe`` multiple times', testEarlyErrorPipe, () => $(earlyErrorOptions)`empty.js`.pipe(earlyErrorOptions)`empty.js`.pipe`empty.js`); - -const testEarlyErrorConvertor = async (t, streamMethod) => { - const subprocess = getEarlyErrorSubprocess(); - const stream = subprocess[streamMethod](); - stream.on('close', () => {}); - stream.read?.(); - stream.write?.('.'); - await t.throwsAsync(subprocess); -}; - -test('child_process.spawn() early errors can use .readable()', testEarlyErrorConvertor, 'readable'); -test('child_process.spawn() early errors can use .writable()', testEarlyErrorConvertor, 'writable'); -test('child_process.spawn() early errors can use .duplex()', testEarlyErrorConvertor, 'duplex'); - -const testEarlyErrorStream = async (t, getStreamProperty, options) => { - const subprocess = getEarlyErrorSubprocess(options); - const stream = getStreamProperty(subprocess); - stream.on('close', () => {}); - stream.read?.(); - stream.end?.(); - await t.throwsAsync(subprocess); -}; - -test('child_process.spawn() early errors can use .stdin', testEarlyErrorStream, ({stdin}) => stdin); -test('child_process.spawn() early errors can use .stdout', testEarlyErrorStream, ({stdout}) => stdout); -test('child_process.spawn() early errors can use .stderr', testEarlyErrorStream, ({stderr}) => stderr); -test('child_process.spawn() early errors can use .stdio[1]', testEarlyErrorStream, ({stdio}) => stdio[1]); -test('child_process.spawn() early errors can use .stdio[3]', testEarlyErrorStream, ({stdio}) => stdio[3], fullStdio); -test('child_process.spawn() early errors can use .all', testEarlyErrorStream, ({all}) => all, {all: true}); diff --git a/test/return/final-error.js b/test/return/final-error.js deleted file mode 100644 index f622089c3c..0000000000 --- a/test/return/final-error.js +++ /dev/null @@ -1,164 +0,0 @@ -import test from 'ava'; -import { - execa, - execaSync, - ExecaError, - ExecaSyncError, -} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString} from '../helpers/input.js'; -import {getEarlyErrorSubprocess, getEarlyErrorSubprocessSync} from '../helpers/early-error.js'; - -setFixtureDirectory(); - -const testUnusualError = async (t, error, expectedOriginalMessage = String(error)) => { - const subprocess = execa('empty.js'); - subprocess.emit('error', error); - const {originalMessage, shortMessage, message} = await t.throwsAsync(subprocess); - t.is(originalMessage, expectedOriginalMessage === '' ? undefined : expectedOriginalMessage); - t.true(shortMessage.includes(expectedOriginalMessage)); - t.is(message, shortMessage); -}; - -test('error instance can be null', testUnusualError, null); -test('error instance can be false', testUnusualError, false); -test('error instance can be a string', testUnusualError, 'test'); -test('error instance can be a number', testUnusualError, 0); -test('error instance can be a BigInt', testUnusualError, 0n); -test('error instance can be a symbol', testUnusualError, Symbol('test')); -test('error instance can be a function', testUnusualError, () => {}); -test('error instance can be an array', testUnusualError, ['test', 'test']); -// eslint-disable-next-line unicorn/error-message -test('error instance can be an error with an empty message', testUnusualError, new Error(''), ''); -test('error instance can be undefined', testUnusualError, undefined, 'undefined'); - -test('error instance can be a plain object', async t => { - const subprocess = execa('empty.js'); - subprocess.emit('error', {message: foobarString}); - await t.throwsAsync(subprocess, {message: new RegExp(foobarString)}); -}); - -const runAndFail = (t, fixtureName, argument, error) => { - const subprocess = execa(fixtureName, [argument]); - subprocess.emit('error', error); - return t.throwsAsync(subprocess); -}; - -const testErrorCopy = async (t, getPreviousArgument, argument = 'two') => { - const fixtureName = 'empty.js'; - const firstArgument = 'foo'; - - const previousArgument = await getPreviousArgument(t, fixtureName); - const previousError = await runAndFail(t, fixtureName, firstArgument, previousArgument); - const error = await runAndFail(t, fixtureName, argument, previousError); - const message = `Command failed: ${fixtureName} ${argument}\n${foobarString}`; - - t.not(error, previousError); - t.is(error.cause, previousError); - t.is(error.command, `${fixtureName} ${argument}`); - t.is(error.message, message); - t.true(error.stack.includes(message)); - t.is(error.shortMessage, message); - t.is(error.originalMessage, foobarString); -}; - -test('error instance can be shared', testErrorCopy, () => new Error(foobarString)); -test('error TypeError can be shared', testErrorCopy, () => new TypeError(foobarString)); -test('error string can be shared', testErrorCopy, () => foobarString); -test('error copy can be shared', testErrorCopy, (t, fixtureName) => runAndFail(t, fixtureName, 'bar', new Error(foobarString))); -test('error with same message can be shared', testErrorCopy, () => new Error(foobarString), 'foo'); - -test('error.cause is not set if error.exitCode is not 0', async t => { - const {exitCode, cause} = await t.throwsAsync(execa('fail.js')); - t.is(exitCode, 2); - t.is(cause, undefined); -}); - -test('error.cause is not set if error.isTerminated', async t => { - const subprocess = execa('forever.js'); - subprocess.kill(); - const {isTerminated, cause} = await t.throwsAsync(subprocess); - t.true(isTerminated); - t.is(cause, undefined); -}); - -test('error.cause is not set if error.timedOut', async t => { - const {timedOut, cause} = await t.throwsAsync(execa('forever.js', {timeout: 1})); - t.true(timedOut); - t.is(cause, undefined); -}); - -test('error.cause is set on error event', async t => { - const subprocess = execa('empty.js'); - const error = new Error(foobarString); - subprocess.emit('error', error); - const {cause} = await t.throwsAsync(subprocess); - t.is(cause, error); -}); - -test('error.cause is set if error.isCanceled', async t => { - const controller = new AbortController(); - const subprocess = execa('forever.js', {cancelSignal: controller.signal}); - const cause = new Error('test'); - controller.abort(cause); - const error = await t.throwsAsync(subprocess); - t.true(error.isCanceled); - t.true(error.isTerminated); - t.is(error.signal, 'SIGTERM'); - t.is(error.cause, cause); -}); - -test('error.cause is not set if error.isTerminated with .kill(error)', async t => { - const subprocess = execa('forever.js'); - const error = new Error('test'); - subprocess.kill(error); - const {isTerminated, cause} = await t.throwsAsync(subprocess); - t.true(isTerminated); - t.is(cause, error); -}); - -test('Error is instanceof ExecaError', async t => { - await t.throwsAsync(execa('fail.js'), {instanceOf: ExecaError}); -}); - -test('Early error is instanceof ExecaError', async t => { - await t.throwsAsync(getEarlyErrorSubprocess(), {instanceOf: ExecaError}); -}); - -test('Error is instanceof ExecaSyncError', t => { - t.throws(() => { - execaSync('fail.js'); - }, {instanceOf: ExecaSyncError}); -}); - -test('Early error is instanceof ExecaSyncError', t => { - t.throws(() => { - getEarlyErrorSubprocessSync(); - }, {instanceOf: ExecaSyncError}); -}); - -test('Pipe error is instanceof ExecaError', async t => { - await t.throwsAsync(execa('empty.js').pipe(false), {instanceOf: ExecaError}); -}); - -const assertNameShape = (t, error) => { - t.false(Object.hasOwn(error, 'name')); - t.true(Object.hasOwn(Object.getPrototypeOf(error), 'name')); - t.false(propertyIsEnumerable.call(Object.getPrototypeOf(error), 'name')); -}; - -const {propertyIsEnumerable} = Object.prototype; - -test('error.name is properly set', async t => { - const error = await t.throwsAsync(execa('fail.js')); - t.is(error.name, 'ExecaError'); - assertNameShape(t, error); -}); - -test('error.name is properly set - sync', async t => { - const error = await t.throws(() => { - execaSync('fail.js'); - }); - t.is(error.name, 'ExecaSyncError'); - assertNameShape(t, error); -}); diff --git a/test/return/message.js b/test/return/message.js deleted file mode 100644 index 0642c2497e..0000000000 --- a/test/return/message.js +++ /dev/null @@ -1,118 +0,0 @@ -import test from 'ava'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {fullStdio, getStdio} from '../helpers/stdio.js'; -import {foobarString, foobarObject, foobarObjectInspect} from '../helpers/input.js'; -import {QUOTE} from '../helpers/verbose.js'; -import {noopGenerator, outputObjectGenerator} from '../helpers/generator.js'; - -setFixtureDirectory(); - -test('error.message contains the command', async t => { - await t.throwsAsync(execa('exit.js', ['2', 'foo', 'bar']), {message: /exit.js 2 foo bar/}); -}); - -// eslint-disable-next-line max-params -const testStdioMessage = async (t, encoding, all, objectMode, execaMethod) => { - const {exitCode, message} = await execaMethod('echo-fail.js', { - ...getStdio(1, noopGenerator(objectMode, false, true), 4), - encoding, - all, - reject: false, - }); - t.is(exitCode, 1); - const output = all ? 'stdout\nstderr' : 'stderr\n\nstdout'; - t.true(message.endsWith(`echo-fail.js\n\n${output}\n\nfd3`)); -}; - -test('error.message contains stdout/stderr/stdio if available', testStdioMessage, 'utf8', false, false, execa); -test('error.message contains stdout/stderr/stdio even with encoding "buffer"', testStdioMessage, 'buffer', false, false, execa); -test('error.message contains all if available', testStdioMessage, 'utf8', true, false, execa); -test('error.message contains all even with encoding "buffer"', testStdioMessage, 'buffer', true, false, execa); -test('error.message contains stdout/stderr/stdio if available, objectMode', testStdioMessage, 'utf8', false, true, execa); -test('error.message contains stdout/stderr/stdio even with encoding "buffer", objectMode', testStdioMessage, 'buffer', false, true, execa); -test('error.message contains all if available, objectMode', testStdioMessage, 'utf8', true, true, execa); -test('error.message contains all even with encoding "buffer", objectMode', testStdioMessage, 'buffer', true, true, execa); -test('error.message contains stdout/stderr/stdio if available, sync', testStdioMessage, 'utf8', false, false, execaSync); -test('error.message contains stdout/stderr/stdio even with encoding "buffer", sync', testStdioMessage, 'buffer', false, false, execaSync); -test('error.message contains all if available, sync', testStdioMessage, 'utf8', true, false, execaSync); -test('error.message contains all even with encoding "buffer", sync', testStdioMessage, 'buffer', true, false, execaSync); -test('error.message contains stdout/stderr/stdio if available, objectMode, sync', testStdioMessage, 'utf8', false, true, execaSync); -test('error.message contains stdout/stderr/stdio even with encoding "buffer", objectMode, sync', testStdioMessage, 'buffer', false, true, execaSync); -test('error.message contains all if available, objectMode, sync', testStdioMessage, 'utf8', true, true, execaSync); -test('error.message contains all even with encoding "buffer", objectMode, sync', testStdioMessage, 'buffer', true, true, execaSync); - -const testLinesMessage = async (t, encoding, stripFinalNewline, execaMethod) => { - const {failed, message} = await execaMethod('noop-fail.js', ['1', `${foobarString}\n${foobarString}\n`], { - lines: true, - encoding, - stripFinalNewline, - reject: false, - }); - t.true(failed); - t.true(message.endsWith(`noop-fail.js 1 ${QUOTE}${foobarString}\\n${foobarString}\\n${QUOTE}\n\n${foobarString}\n${foobarString}`)); -}; - -test('error.message handles "lines: true"', testLinesMessage, 'utf8', false, execa); -test('error.message handles "lines: true", stripFinalNewline', testLinesMessage, 'utf8', true, execa); -test('error.message handles "lines: true", buffer', testLinesMessage, 'buffer', false, execa); -test('error.message handles "lines: true", buffer, stripFinalNewline', testLinesMessage, 'buffer', true, execa); -test('error.message handles "lines: true", sync', testLinesMessage, 'utf8', false, execaSync); -test('error.message handles "lines: true", stripFinalNewline, sync', testLinesMessage, 'utf8', true, execaSync); -test('error.message handles "lines: true", buffer, sync', testLinesMessage, 'buffer', false, execaSync); -test('error.message handles "lines: true", buffer, stripFinalNewline, sync', testLinesMessage, 'buffer', true, execaSync); - -const testPartialIgnoreMessage = async (t, fdNumber, stdioOption, output) => { - const {message} = await t.throwsAsync(execa('echo-fail.js', getStdio(fdNumber, stdioOption, 4))); - t.true(message.endsWith(`echo-fail.js\n\n${output}\n\nfd3`)); -}; - -test('error.message does not contain stdout if not available', testPartialIgnoreMessage, 1, 'ignore', 'stderr'); -test('error.message does not contain stderr if not available', testPartialIgnoreMessage, 2, 'ignore', 'stdout'); -test('error.message does not contain stdout if it is an object', testPartialIgnoreMessage, 1, outputObjectGenerator(), 'stderr'); -test('error.message does not contain stderr if it is an object', testPartialIgnoreMessage, 2, outputObjectGenerator(), 'stdout'); - -const testFullIgnoreMessage = async (t, options, resultProperty) => { - const {[resultProperty]: message} = await t.throwsAsync(execa('echo-fail.js', options)); - t.false(message.includes('stderr')); - t.false(message.includes('stdout')); - t.false(message.includes('fd3')); -}; - -test('error.message does not contain stdout/stderr/stdio if not available', testFullIgnoreMessage, {stdio: 'ignore'}, 'message'); -test('error.shortMessage does not contain stdout/stderr/stdio', testFullIgnoreMessage, fullStdio, 'shortMessage'); - -const testErrorMessageConsistent = async (t, stdout) => { - const {message} = await t.throwsAsync(execa('noop-both-fail-strict.js', [stdout, 'stderr'])); - t.true(message.endsWith(' stderr\n\nstderr\n\nstdout')); -}; - -test('error.message newlines are consistent - no newline', testErrorMessageConsistent, 'stdout'); -test('error.message newlines are consistent - newline', testErrorMessageConsistent, 'stdout\n'); - -test('Original error.message is kept', async t => { - const {originalMessage} = await t.throwsAsync(execa('noop.js', {uid: true})); - t.is(originalMessage, 'The "options.uid" property must be int32. Received type boolean (true)'); -}); - -const testIpcOutput = async (t, doubles, ipcInput, returnedMessage) => { - const fixtureName = doubles ? 'ipc-echo-twice-fail.js' : 'ipc-echo-fail.js'; - const {exitCode, message, ipcOutput} = await t.throwsAsync(execa(fixtureName, {ipcInput})); - t.is(exitCode, 1); - t.true(message.endsWith(`\n\n${doubles ? `${returnedMessage}\n${returnedMessage}` : returnedMessage}`)); - t.deepEqual(ipcOutput, doubles ? [ipcInput, ipcInput] : [ipcInput]); -}; - -test('error.message contains IPC messages, single string', testIpcOutput, false, foobarString, foobarString); -test('error.message contains IPC messages, two strings', testIpcOutput, true, foobarString, foobarString); -test('error.message contains IPC messages, single object', testIpcOutput, false, foobarObject, foobarObjectInspect); -test('error.message contains IPC messages, two objects', testIpcOutput, true, foobarObject, foobarObjectInspect); -test('error.message contains IPC messages, multiline string', testIpcOutput, false, `${foobarString}\n${foobarString}`, `${foobarString}\n${foobarString}`); -test('error.message contains IPC messages, control characters', testIpcOutput, false, '\0', '\\u0000'); - -test('error.message does not contain IPC messages, buffer false', async t => { - const {exitCode, message, ipcOutput} = await t.throwsAsync(execa('ipc-echo-fail.js', {ipcInput: foobarString, buffer: false})); - t.is(exitCode, 1); - t.true(message.endsWith('ipc-echo-fail.js')); - t.deepEqual(ipcOutput, []); -}); diff --git a/test/return/output.js b/test/return/output.js deleted file mode 100644 index edc446ff0e..0000000000 --- a/test/return/output.js +++ /dev/null @@ -1,135 +0,0 @@ -import test from 'ava'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {fullStdio, getStdio} from '../helpers/stdio.js'; -import {foobarString} from '../helpers/input.js'; -import { - getEarlyErrorSubprocess, - getEarlyErrorSubprocessSync, - expectedEarlyError, - expectedEarlyErrorSync, -} from '../helpers/early-error.js'; - -setFixtureDirectory(); - -const testOutput = async (t, fdNumber, execaMethod) => { - const {stdout, stderr, stdio} = await execaMethod('noop-fd.js', [`${fdNumber}`, foobarString], fullStdio); - t.is(stdio[fdNumber], foobarString); - - if (fdNumber === 1) { - t.is(stdio[fdNumber], stdout); - } else if (fdNumber === 2) { - t.is(stdio[fdNumber], stderr); - } -}; - -test('can return stdout', testOutput, 1, execa); -test('can return stderr', testOutput, 2, execa); -test('can return output stdio[*]', testOutput, 3, execa); -test('can return stdout, sync', testOutput, 1, execaSync); -test('can return stderr, sync', testOutput, 2, execaSync); -test('can return output stdio[*], sync', testOutput, 3, execaSync); - -const testNoStdin = async (t, execaMethod) => { - const {stdio} = await execaMethod('noop.js', [foobarString]); - t.is(stdio[0], undefined); -}; - -test('cannot return stdin', testNoStdin, execa); -test('cannot return stdin, sync', testNoStdin, execaSync); - -test('cannot return input stdio[*]', async t => { - const {stdio} = await execa('stdin-fd.js', ['3'], getStdio(3, [[foobarString]])); - t.is(stdio[3], undefined); -}); - -test('do not try to consume streams twice', async t => { - const subprocess = execa('noop.js', ['foo']); - const {stdout} = await subprocess; - const {stdout: stdout2} = await subprocess; - t.is(stdout, 'foo'); - t.is(stdout2, 'foo'); -}); - -const testEmptyErrorStdio = async (t, execaMethod) => { - const {failed, stdout, stderr, stdio} = await execaMethod('fail.js', {reject: false}); - t.true(failed); - t.is(stdout, ''); - t.is(stderr, ''); - t.deepEqual(stdio, [undefined, '', '']); -}; - -test('empty error.stdout/stderr/stdio', testEmptyErrorStdio, execa); -test('empty error.stdout/stderr/stdio, sync', testEmptyErrorStdio, execaSync); - -const testUndefinedErrorStdio = async (t, execaMethod) => { - const {stdout, stderr, stdio} = await execaMethod('empty.js', {stdio: 'ignore'}); - t.is(stdout, undefined); - t.is(stderr, undefined); - t.deepEqual(stdio, [undefined, undefined, undefined]); -}; - -test('undefined error.stdout/stderr/stdio', testUndefinedErrorStdio, execa); -test('undefined error.stdout/stderr/stdio, sync', testUndefinedErrorStdio, execaSync); - -const testEmptyAll = async (t, options, expectedValue, execaMethod) => { - const {all} = await execaMethod('empty.js', options); - t.is(all, expectedValue); -}; - -test('empty error.all', testEmptyAll, {all: true}, '', execa); -test('undefined error.all', testEmptyAll, {}, undefined, execa); -test('ignored error.all', testEmptyAll, {all: true, stdio: 'ignore'}, undefined, execa); -test('empty error.all, sync', testEmptyAll, {all: true}, '', execaSync); -test('undefined error.all, sync', testEmptyAll, {}, undefined, execaSync); -test('ignored error.all, sync', testEmptyAll, {all: true, stdio: 'ignore'}, undefined, execaSync); - -test('empty error.stdio[0] even with input', async t => { - const {stdio} = await t.throwsAsync(execa('fail.js', {input: 'test'})); - t.is(stdio[0], undefined); -}); - -const validateSpawnErrorStdio = (t, {stdout, stderr, stdio, all}) => { - t.is(stdout, undefined); - t.is(stderr, undefined); - t.is(all, undefined); - t.deepEqual(stdio, [undefined, undefined, undefined]); -}; - -test('stdout/stderr/all/stdio on subprocess spawning errors', async t => { - const error = await t.throwsAsync(getEarlyErrorSubprocess({all: true})); - t.like(error, expectedEarlyError); - validateSpawnErrorStdio(t, error); -}); - -test('stdout/stderr/all/stdio on subprocess spawning errors, sync', t => { - const error = t.throws(() => getEarlyErrorSubprocessSync({all: true})); - t.like(error, expectedEarlyErrorSync); - validateSpawnErrorStdio(t, error); -}); - -const testErrorOutput = async (t, execaMethod) => { - const {failed, stdout, stderr, stdio} = await execaMethod('echo-fail.js', {...fullStdio, reject: false}); - t.true(failed); - t.is(stdout, 'stdout'); - t.is(stderr, 'stderr'); - t.deepEqual(stdio, [undefined, 'stdout', 'stderr', 'fd3']); -}; - -test('error.stdout/stderr/stdio is defined', testErrorOutput, execa); -test('error.stdout/stderr/stdio is defined, sync', testErrorOutput, execaSync); - -test('ipc on subprocess spawning errors', async t => { - const error = await t.throwsAsync(getEarlyErrorSubprocess({ipc: true})); - t.like(error, expectedEarlyError); - t.deepEqual(error.ipcOutput, []); -}); - -const testEarlyErrorNoIpc = async (t, options) => { - const error = await t.throwsAsync(getEarlyErrorSubprocess(options)); - t.like(error, expectedEarlyError); - t.deepEqual(error.ipcOutput, []); -}; - -test('ipc on subprocess spawning errors, ipc false', testEarlyErrorNoIpc, {ipc: false}); -test('ipc on subprocess spawning errors, buffer false', testEarlyErrorNoIpc, {buffer: false}); diff --git a/test/return/reject.js b/test/return/reject.js deleted file mode 100644 index 186a8b7b7f..0000000000 --- a/test/return/reject.js +++ /dev/null @@ -1,15 +0,0 @@ -import test from 'ava'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; - -setFixtureDirectory(); - -test('skip throwing when using reject option', async t => { - const {exitCode} = await execa('fail.js', {reject: false}); - t.is(exitCode, 2); -}); - -test('skip throwing when using reject option in sync mode', t => { - const {exitCode} = execaSync('fail.js', {reject: false}); - t.is(exitCode, 2); -}); diff --git a/test/return/result.js b/test/return/result.js deleted file mode 100644 index 30d8d4cfcb..0000000000 --- a/test/return/result.js +++ /dev/null @@ -1,147 +0,0 @@ -import process from 'node:process'; -import test from 'ava'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {fullStdio} from '../helpers/stdio.js'; - -const isWindows = process.platform === 'win32'; - -setFixtureDirectory(); - -const testSuccessShape = async (t, execaMethod) => { - const result = await execaMethod('empty.js', {...fullStdio, all: true}); - t.deepEqual(Reflect.ownKeys(result), [ - 'command', - 'escapedCommand', - 'cwd', - 'durationMs', - 'failed', - 'timedOut', - 'isCanceled', - 'isGracefullyCanceled', - 'isTerminated', - 'isMaxBuffer', - 'isForcefullyTerminated', - 'exitCode', - 'stdout', - 'stderr', - 'all', - 'stdio', - 'ipcOutput', - 'pipedFrom', - ]); -}; - -test('Return value properties are not missing and are ordered', testSuccessShape, execa); -test('Return value properties are not missing and are ordered, sync', testSuccessShape, execaSync); - -const testErrorShape = async (t, execaMethod) => { - const error = await execaMethod('fail.js', {...fullStdio, all: true, reject: false}); - t.is(error.exitCode, 2); - t.deepEqual(Reflect.ownKeys(error), [ - 'stack', - 'message', - 'shortMessage', - 'command', - 'escapedCommand', - 'cwd', - 'durationMs', - 'failed', - 'timedOut', - 'isCanceled', - 'isGracefullyCanceled', - 'isTerminated', - 'isMaxBuffer', - 'isForcefullyTerminated', - 'exitCode', - 'stdout', - 'stderr', - 'all', - 'stdio', - 'ipcOutput', - 'pipedFrom', - ]); -}; - -test('Error properties are not missing and are ordered', testErrorShape, execa); -test('Error properties are not missing and are ordered, sync', testErrorShape, execaSync); - -test('failed is false on success', async t => { - const {failed} = await execa('noop.js', ['foo']); - t.false(failed); -}); - -test('failed is true on failure', async t => { - const {failed} = await t.throwsAsync(execa('fail.js')); - t.true(failed); -}); - -test('error.isTerminated is true if subprocess was killed directly', async t => { - const subprocess = execa('forever.js', {killSignal: 'SIGINT'}); - - subprocess.kill(); - - const {isTerminated, signal, originalMessage, message, shortMessage} = await t.throwsAsync(subprocess, {message: /was killed with SIGINT/}); - t.true(isTerminated); - t.is(signal, 'SIGINT'); - t.is(originalMessage, undefined); - t.is(shortMessage, 'Command was killed with SIGINT (User interruption with CTRL-C): forever.js'); - t.is(message, shortMessage); -}); - -test('error.isTerminated is true if subprocess was killed indirectly', async t => { - const subprocess = execa('forever.js', {killSignal: 'SIGHUP'}); - - process.kill(subprocess.pid, 'SIGINT'); - - // `subprocess.kill()` is emulated by Node.js on Windows - if (isWindows) { - const {isTerminated, signal} = await t.throwsAsync(subprocess, {message: /failed with exit code 1/}); - t.is(isTerminated, false); - t.is(signal, undefined); - } else { - const {isTerminated, signal} = await t.throwsAsync(subprocess, {message: /was killed with SIGINT/}); - t.is(isTerminated, true); - t.is(signal, 'SIGINT'); - } -}); - -test('result.isTerminated is false if not killed', async t => { - const {isTerminated} = await execa('noop.js'); - t.false(isTerminated); -}); - -test('result.isTerminated is false if not killed and subprocess.kill() was called', async t => { - const subprocess = execa('noop.js'); - subprocess.kill(0); - t.true(subprocess.killed); - const {isTerminated} = await subprocess; - t.false(isTerminated); -}); - -test('result.isTerminated is false if not killed, in sync mode', t => { - const {isTerminated} = execaSync('noop.js'); - t.false(isTerminated); -}); - -test('result.isTerminated is false on subprocess error', async t => { - const {isTerminated} = await t.throwsAsync(execa('wrong command')); - t.false(isTerminated); -}); - -test('result.isTerminated is false on subprocess error, in sync mode', t => { - const {isTerminated} = t.throws(() => { - execaSync('wrong command'); - }); - t.false(isTerminated); -}); - -test('error.code is undefined on success', async t => { - const {code} = await execa('noop.js'); - t.is(code, undefined); -}); - -test('error.code is defined on failure if applicable', async t => { - const {code} = await t.throwsAsync(execa('noop.js', {uid: true})); - t.is(code, 'ERR_INVALID_ARG_TYPE'); -}); diff --git a/test/stdio/direction.js b/test/stdio/direction.js deleted file mode 100644 index 905bfb2eda..0000000000 --- a/test/stdio/direction.js +++ /dev/null @@ -1,51 +0,0 @@ -import {readFile, writeFile, rm} from 'node:fs/promises'; -import process from 'node:process'; -import test from 'ava'; -import tempfile from 'tempfile'; -import {execa, execaSync} from '../../index.js'; -import {getStdio} from '../helpers/stdio.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString} from '../helpers/input.js'; - -setFixtureDirectory(); - -const testInputOutput = (t, stdioOption, execaMethod) => { - t.throws(() => { - execaMethod('empty.js', getStdio(3, [new ReadableStream(), stdioOption])); - }, {message: /readable and writable/}); -}; - -test('Cannot pass both readable and writable values to stdio[*] - WritableStream', testInputOutput, new WritableStream(), execa); -test('Cannot pass both readable and writable values to stdio[*] - 1', testInputOutput, 1, execa); -test('Cannot pass both readable and writable values to stdio[*] - 2', testInputOutput, 2, execa); -test('Cannot pass both readable and writable values to stdio[*] - process.stdout', testInputOutput, process.stdout, execa); -test('Cannot pass both readable and writable values to stdio[*] - process.stderr', testInputOutput, process.stderr, execa); -test('Cannot pass both readable and writable values to stdio[*] - WritableStream - sync', testInputOutput, new WritableStream(), execaSync); -test('Cannot pass both readable and writable values to stdio[*] - 1 - sync', testInputOutput, 1, execaSync); -test('Cannot pass both readable and writable values to stdio[*] - 2 - sync', testInputOutput, 2, execaSync); -test('Cannot pass both readable and writable values to stdio[*] - process.stdout - sync', testInputOutput, process.stdout, execaSync); -test('Cannot pass both readable and writable values to stdio[*] - process.stderr - sync', testInputOutput, process.stderr, execaSync); - -const testAmbiguousDirection = async (t, execaMethod) => { - const [filePathOne, filePathTwo] = [tempfile(), tempfile()]; - await execaMethod('noop-fd.js', ['3', foobarString], getStdio(3, [{file: filePathOne}, {file: filePathTwo}])); - t.deepEqual( - await Promise.all([readFile(filePathOne, 'utf8'), readFile(filePathTwo, 'utf8')]), - [foobarString, foobarString], - ); - await Promise.all([rm(filePathOne), rm(filePathTwo)]); -}; - -test('stdio[*] default direction is output', testAmbiguousDirection, execa); -test('stdio[*] default direction is output - sync', testAmbiguousDirection, execaSync); - -const testAmbiguousMultiple = async (t, fdNumber) => { - const filePath = tempfile(); - await writeFile(filePath, foobarString); - const {stdout} = await execa('stdin-fd.js', [`${fdNumber}`], getStdio(fdNumber, [{file: filePath}, ['foo', 'bar']])); - t.is(stdout, `${foobarString}${foobarString}`); - await rm(filePath); -}; - -test('stdin ambiguous direction is influenced by other values', testAmbiguousMultiple, 0); -test('stdio[*] ambiguous direction is influenced by other values', testAmbiguousMultiple, 3); diff --git a/test/stdio/duplex.js b/test/stdio/duplex.js deleted file mode 100644 index 8b2588fe11..0000000000 --- a/test/stdio/duplex.js +++ /dev/null @@ -1,62 +0,0 @@ -import {createHash} from 'node:crypto'; -import {promisify} from 'node:util'; -import {createGzip, gunzip} from 'node:zlib'; -import test from 'ava'; -import {execa} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import { - foobarString, - foobarObject, - foobarUppercase, - foobarUppercaseHex, -} from '../helpers/input.js'; -import {uppercaseEncodingDuplex, getOutputDuplex} from '../helpers/duplex.js'; - -setFixtureDirectory(); - -test('Can use crypto.createHash()', async t => { - const {stdout} = await execa('noop-fd.js', ['1', foobarString], {stdout: {transform: createHash('sha1')}, encoding: 'hex'}); - const expectedStdout = createHash('sha1').update(foobarString).digest('hex'); - t.is(stdout, expectedStdout); -}); - -test('Can use zlib.createGzip()', async t => { - const {stdout} = await execa('noop-fd.js', ['1', foobarString], {stdout: {transform: createGzip()}, encoding: 'buffer'}); - const decompressedStdout = await promisify(gunzip)(stdout); - t.is(decompressedStdout.toString(), foobarString); -}); - -test('Can use encoding "hex"', async t => { - const {transform} = uppercaseEncodingDuplex('hex')(); - t.is(transform.readableEncoding, 'hex'); - const {stdout} = await execa('noop-fd.js', ['1', foobarString], {stdout: {transform}}); - t.is(stdout, foobarUppercaseHex); -}); - -test('Cannot use objectMode: true with duplex.readableObjectMode: false', t => { - t.throws(() => { - execa('noop-fd.js', ['1', foobarString], {stdout: uppercaseEncodingDuplex(undefined, false)(true)}); - }, {message: /cannot be `false` if `new Duplex\({objectMode: true}\)`/}); -}); - -test('Cannot use objectMode: false with duplex.readableObjectMode: true', t => { - t.throws(() => { - execa('noop-fd.js', ['1', foobarString], {stdout: uppercaseEncodingDuplex(undefined, true)(false)}); - }, {message: /can only be `true` if `new Duplex\({objectMode: true}\)`/}); -}); - -const testObjectModeFalse = async (t, objectMode) => { - const {stdout} = await execa('noop-fd.js', ['1', foobarString], {stdout: uppercaseEncodingDuplex(undefined, objectMode)(false)}); - t.is(stdout, foobarUppercase); -}; - -test('Can use objectMode: false with duplex.readableObjectMode: false', testObjectModeFalse, false); -test('Can use objectMode: undefined with duplex.readableObjectMode: false', testObjectModeFalse, undefined); - -const testObjectModeTrue = async (t, objectMode) => { - const {stdout} = await execa('noop-fd.js', ['1', foobarString], {stdout: getOutputDuplex(foobarObject, objectMode)(true)}); - t.deepEqual(stdout, [foobarObject]); -}; - -test('Can use objectMode: true with duplex.readableObjectMode: true', testObjectModeTrue, true); -test('Can use objectMode: undefined with duplex.readableObjectMode: true', testObjectModeTrue, undefined); diff --git a/test/stdio/duplicate.js b/test/stdio/duplicate.js deleted file mode 100644 index e7eefa9215..0000000000 --- a/test/stdio/duplicate.js +++ /dev/null @@ -1,213 +0,0 @@ -import {once} from 'node:events'; -import {createReadStream, createWriteStream} from 'node:fs'; -import {readFile, writeFile, rm} from 'node:fs/promises'; -import {Readable, Writable} from 'node:stream'; -import {pathToFileURL} from 'node:url'; -import test from 'ava'; -import tempfile from 'tempfile'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import { - uppercaseGenerator, - appendGenerator, - appendAsyncGenerator, - casedSuffix, -} from '../helpers/generator.js'; -import {appendDuplex} from '../helpers/duplex.js'; -import {appendWebTransform} from '../helpers/web-transform.js'; -import {foobarString, foobarUint8Array, foobarUppercase} from '../helpers/input.js'; -import {fullStdio} from '../helpers/stdio.js'; -import {nestedSubprocess} from '../helpers/nested.js'; -import {getAbsolutePath} from '../helpers/file-path.js'; -import {noopDuplex} from '../helpers/stream.js'; - -setFixtureDirectory(); - -const getNativeStream = stream => stream; -const getNonNativeStream = stream => ['pipe', stream]; -const getWebWritableStream = stream => Writable.toWeb(stream); - -const getDummyDuplex = () => ({transform: noopDuplex()}); -const getDummyWebTransformStream = () => new TransformStream(); - -const getDummyPath = async () => { - const filePath = tempfile(); - await writeFile(filePath, ''); - return filePath; -}; - -const getDummyFilePath = async () => ({file: await getDummyPath()}); -const getDummyFileURL = async () => pathToFileURL((await getDummyPath())); -const duplexName = 'a Duplex stream'; -const webTransformName = 'a web TransformStream'; -const filePathName = 'a file path string'; -const fileURLName = 'a file URL'; - -const getDifferentInputs = stdioOption => ({stdio: [stdioOption, 'pipe', 'pipe', stdioOption]}); -const getDifferentOutputs = stdioOption => ({stdout: stdioOption, stderr: stdioOption}); -const getDifferentInputsOutputs = stdioOption => ({stdin: stdioOption, stdout: stdioOption}); -const differentInputsName = '`stdin` and `stdio[3]`'; -const differentOutputsName = '`stdout` and `stderr`'; -const differentInputsOutputsName = '`stdin` and `stdout`'; - -test('Can use multiple "pipe" on same input file descriptor', async t => { - const subprocess = execa('stdin.js', {stdin: ['pipe', 'pipe']}); - subprocess.stdin.end(foobarString); - const {stdout} = await subprocess; - t.is(stdout, foobarString); -}); - -const testTwoPipeOutput = async (t, execaMethod) => { - const {stdout} = await execaMethod('noop.js', [foobarString], {stdout: ['pipe', 'pipe']}); - t.is(stdout, foobarString); -}; - -test('Can use multiple "pipe" on same output file descriptor', testTwoPipeOutput, execa); -test('Can use multiple "pipe" on same output file descriptor, sync', testTwoPipeOutput, execaSync); - -test('Can repeat same stream on same input file descriptor', async t => { - const stream = Readable.from([foobarString]); - const {stdout} = await execa('stdin.js', {stdin: ['pipe', stream, stream]}); - t.is(stdout, foobarString); -}); - -test('Can repeat same stream on same output file descriptor', async t => { - let stdout = ''; - const stream = new Writable({ - write(chunk, encoding, done) { - stdout += chunk.toString(); - done(); - }, - }); - await execa('noop-fd.js', ['1', foobarString], {stdout: ['pipe', stream, stream]}); - t.is(stdout, foobarString); -}); - -// eslint-disable-next-line max-params -const testTwoGenerators = async (t, producesTwo, execaMethod, firstGenerator, secondGenerator = firstGenerator) => { - const {stdout} = await execaMethod('noop-fd.js', ['1', foobarString], {stdout: [firstGenerator, secondGenerator]}); - const expectedSuffix = producesTwo ? `${casedSuffix}${casedSuffix}` : casedSuffix; - t.is(stdout, `${foobarString}${expectedSuffix}`); -}; - -test('Can use multiple identical generators', testTwoGenerators, true, execa, appendGenerator().transform); -test('Can use multiple identical generators, options object', testTwoGenerators, true, execa, appendGenerator()); -test('Can use multiple identical generators, async', testTwoGenerators, true, execa, appendAsyncGenerator().transform); -test('Can use multiple identical generators, options object, async', testTwoGenerators, true, execa, appendAsyncGenerator()); -test('Can use multiple identical generators, sync', testTwoGenerators, true, execaSync, appendGenerator().transform); -test('Can use multiple identical generators, options object, sync', testTwoGenerators, true, execaSync, appendGenerator()); -test('Ignore duplicate identical duplexes', testTwoGenerators, false, execa, appendDuplex()); -test('Ignore duplicate identical webTransforms', testTwoGenerators, false, execa, appendWebTransform()); -test('Can use multiple generators with duplexes', testTwoGenerators, true, execa, appendGenerator(false, false, true), appendDuplex()); -test('Can use multiple generators with webTransforms', testTwoGenerators, true, execa, appendGenerator(false, false, true), appendWebTransform()); -test('Can use multiple duplexes with webTransforms', testTwoGenerators, true, execa, appendDuplex(), appendWebTransform()); - -const testMultiplePipeOutput = async (t, execaMethod) => { - const {stdout, stderr} = await execaMethod('noop-both.js', [foobarString], fullStdio); - t.is(stdout, foobarString); - t.is(stderr, foobarString); -}; - -test('Can use multiple "pipe" on different output file descriptors', testMultiplePipeOutput, execa); -test('Can use multiple "pipe" on different output file descriptors, sync', testMultiplePipeOutput, execaSync); - -test('Can re-use same generator on different input file descriptors', async t => { - const {stdout} = await execa('stdin-fd-both.js', ['3'], getDifferentInputs([foobarUint8Array, uppercaseGenerator(false, false, true)])); - t.is(stdout, `${foobarUppercase}${foobarUppercase}`); -}); - -test('Can re-use same generator on different output file descriptors', async t => { - const {stdout, stderr} = await execa('noop-both.js', [foobarString], getDifferentOutputs(uppercaseGenerator(false, false, true))); - t.is(stdout, foobarUppercase); - t.is(stderr, foobarUppercase); -}); - -test('Can re-use same non-native Readable stream on different input file descriptors', async t => { - const filePath = tempfile(); - await writeFile(filePath, foobarString); - const stream = createReadStream(filePath); - await once(stream, 'open'); - const {stdout} = await execa('stdin-fd-both.js', ['3'], getDifferentInputs([new Uint8Array(0), stream])); - t.is(stdout, `${foobarString}${foobarString}`); - await rm(filePath); -}); - -const testMultipleStreamOutput = async (t, getStreamOption) => { - const filePath = tempfile(); - const stream = createWriteStream(filePath); - await once(stream, 'open'); - await execa('noop-both.js', [foobarString], getDifferentOutputs(getStreamOption(stream))); - t.is(await readFile(filePath, 'utf8'), `${foobarString}\n${foobarString}\n`); - await rm(filePath); -}; - -test('Can re-use same native Writable stream on different output file descriptors', testMultipleStreamOutput, getNativeStream); -test('Can re-use same non-native Writable stream on different output file descriptors', testMultipleStreamOutput, getNonNativeStream); -test('Can re-use same web Writable stream on different output file descriptors', testMultipleStreamOutput, getWebWritableStream); - -const testMultipleInheritOutput = async (t, isSync) => { - const {stdout} = await nestedSubprocess('noop-both.js', [foobarString], {...getDifferentOutputs(1), isSync}); - t.is(stdout, `${foobarString}\n${foobarString}`); -}; - -test('Can re-use same parent file descriptor on different output file descriptors', testMultipleInheritOutput, false); -test('Can re-use same parent file descriptor on different output file descriptors, sync', testMultipleInheritOutput, true); - -const testMultipleFileInput = async (t, mapFile) => { - const filePath = tempfile(); - await writeFile(filePath, foobarString); - const {stdout} = await execa('stdin-fd-both.js', ['3'], getDifferentInputs([new Uint8Array(0), mapFile(filePath)])); - t.is(stdout, `${foobarString}${foobarString}`); - await rm(filePath); -}; - -test('Can re-use same file path on different input file descriptors', testMultipleFileInput, getAbsolutePath); -test('Can re-use same file URL on different input file descriptors', testMultipleFileInput, pathToFileURL); - -const testMultipleFileOutput = async (t, mapFile, execaMethod) => { - const filePath = tempfile(); - await execaMethod('noop-both.js', [foobarString], getDifferentOutputs(mapFile(filePath))); - t.is(await readFile(filePath, 'utf8'), `${foobarString}\n${foobarString}\n`); - await rm(filePath); -}; - -test('Can re-use same file path on different output file descriptors', testMultipleFileOutput, getAbsolutePath, execa); -test('Can re-use same file path on different output file descriptors, sync', testMultipleFileOutput, getAbsolutePath, execaSync); -test('Can re-use same file URL on different output file descriptors', testMultipleFileOutput, pathToFileURL, execa); -test('Can re-use same file URL on different output file descriptors, sync', testMultipleFileOutput, pathToFileURL, execaSync); - -const testMultipleFileInputOutput = async (t, mapFile, execaMethod) => { - const inputFilePath = tempfile(); - const outputFilePath = tempfile(); - await writeFile(inputFilePath, foobarString); - await execaMethod('stdin.js', {stdin: mapFile(inputFilePath), stdout: mapFile(outputFilePath)}); - t.is(await readFile(outputFilePath, 'utf8'), foobarString); - await Promise.all([rm(inputFilePath), rm(outputFilePath)]); -}; - -test('Can use different file paths on different input/output file descriptors', testMultipleFileInputOutput, getAbsolutePath, execa); -test('Can use different file paths on different input/output file descriptors, sync', testMultipleFileInputOutput, getAbsolutePath, execaSync); -test('Can use different file URL on different input/output file descriptors', testMultipleFileInputOutput, pathToFileURL, execa); -test('Can use different file URL on different input/output file descriptors, sync', testMultipleFileInputOutput, pathToFileURL, execaSync); - -// eslint-disable-next-line max-params -const testMultipleInvalid = async (t, getDummyStream, typeName, getStdio, fdName, execaMethod) => { - const stdioOption = await getDummyStream(); - t.throws(() => { - execaMethod('empty.js', getStdio(stdioOption)); - }, {message: `The ${fdName} options must not target ${typeName} that is the same.`}); - if (stdioOption.transform !== undefined) { - t.true(stdioOption.transform.destroyed); - } -}; - -test('Cannot use same Duplex on different input file descriptors', testMultipleInvalid, getDummyDuplex, duplexName, getDifferentInputs, differentInputsName, execa); -test('Cannot use same Duplex on different output file descriptors', testMultipleInvalid, getDummyDuplex, duplexName, getDifferentOutputs, differentOutputsName, execa); -test('Cannot use same Duplex on both input and output file descriptors', testMultipleInvalid, getDummyDuplex, duplexName, getDifferentInputsOutputs, differentInputsOutputsName, execa); -test('Cannot use same TransformStream on different input file descriptors', testMultipleInvalid, getDummyWebTransformStream, webTransformName, getDifferentInputs, differentInputsName, execa); -test('Cannot use same TransformStream on different output file descriptors', testMultipleInvalid, getDummyWebTransformStream, webTransformName, getDifferentOutputs, differentOutputsName, execa); -test('Cannot use same TransformStream on both input and output file descriptors', testMultipleInvalid, getDummyWebTransformStream, webTransformName, getDifferentInputsOutputs, differentInputsOutputsName, execa); -test('Cannot use same file path on both input and output file descriptors', testMultipleInvalid, getDummyFilePath, filePathName, getDifferentInputsOutputs, differentInputsOutputsName, execa); -test('Cannot use same file URL on both input and output file descriptors', testMultipleInvalid, getDummyFileURL, fileURLName, getDifferentInputsOutputs, differentInputsOutputsName, execa); -test('Cannot use same file path on both input and output file descriptors, sync', testMultipleInvalid, getDummyFilePath, filePathName, getDifferentInputsOutputs, differentInputsOutputsName, execaSync); -test('Cannot use same file URL on both input and output file descriptors, sync', testMultipleInvalid, getDummyFileURL, fileURLName, getDifferentInputsOutputs, differentInputsOutputsName, execaSync); diff --git a/test/stdio/file-descriptor.js b/test/stdio/file-descriptor.js deleted file mode 100644 index 6dc2902357..0000000000 --- a/test/stdio/file-descriptor.js +++ /dev/null @@ -1,34 +0,0 @@ -import {readFile, open, rm} from 'node:fs/promises'; -import test from 'ava'; -import tempfile from 'tempfile'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {getStdio} from '../helpers/stdio.js'; - -setFixtureDirectory(); - -const testFileDescriptorOption = async (t, fdNumber, execaMethod) => { - const filePath = tempfile(); - const fileDescriptor = await open(filePath, 'w'); - await execaMethod('noop-fd.js', [`${fdNumber}`, 'foobar'], getStdio(fdNumber, fileDescriptor)); - t.is(await readFile(filePath, 'utf8'), 'foobar'); - await rm(filePath); - await fileDescriptor.close(); -}; - -test('pass `stdout` to a file descriptor', testFileDescriptorOption, 1, execa); -test('pass `stderr` to a file descriptor', testFileDescriptorOption, 2, execa); -test('pass `stdio[*]` to a file descriptor', testFileDescriptorOption, 3, execa); -test('pass `stdout` to a file descriptor - sync', testFileDescriptorOption, 1, execaSync); -test('pass `stderr` to a file descriptor - sync', testFileDescriptorOption, 2, execaSync); -test('pass `stdio[*]` to a file descriptor - sync', testFileDescriptorOption, 3, execaSync); - -const testStdinWrite = async (t, fdNumber) => { - const subprocess = execa('stdin-fd.js', [`${fdNumber}`], getStdio(fdNumber, 'pipe')); - subprocess.stdio[fdNumber].end('unicorns'); - const {stdout} = await subprocess; - t.is(stdout, 'unicorns'); -}; - -test('you can write to subprocess.stdin', testStdinWrite, 0); -test('you can write to subprocess.stdio[*]', testStdinWrite, 3); diff --git a/test/stdio/file-path-error.js b/test/stdio/file-path-error.js deleted file mode 100644 index d54198df73..0000000000 --- a/test/stdio/file-path-error.js +++ /dev/null @@ -1,173 +0,0 @@ -import {readFile, writeFile, rm} from 'node:fs/promises'; -import {pathToFileURL} from 'node:url'; -import test from 'ava'; -import {pathExists} from 'path-exists'; -import tempfile from 'tempfile'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {identity, getStdio} from '../helpers/stdio.js'; -import {foobarString, foobarUppercase} from '../helpers/input.js'; -import { - outputObjectGenerator, - uppercaseGenerator, - serializeGenerator, - throwingGenerator, -} from '../helpers/generator.js'; -import {getAbsolutePath} from '../helpers/file-path.js'; - -setFixtureDirectory(); - -const nonFileUrl = new URL('https://example.com'); - -const testStdioNonFileUrl = (t, fdNumber, execaMethod) => { - t.throws(() => { - execaMethod('empty.js', getStdio(fdNumber, nonFileUrl)); - }, {message: /pathToFileURL/}); -}; - -test('inputFile cannot be a non-file URL', testStdioNonFileUrl, 'inputFile', execa); -test('stdin cannot be a non-file URL', testStdioNonFileUrl, 0, execa); -test('stdout cannot be a non-file URL', testStdioNonFileUrl, 1, execa); -test('stderr cannot be a non-file URL', testStdioNonFileUrl, 2, execa); -test('stdio[*] cannot be a non-file URL', testStdioNonFileUrl, 3, execa); -test('inputFile cannot be a non-file URL - sync', testStdioNonFileUrl, 'inputFile', execaSync); -test('stdin cannot be a non-file URL - sync', testStdioNonFileUrl, 0, execaSync); -test('stdout cannot be a non-file URL - sync', testStdioNonFileUrl, 1, execaSync); -test('stderr cannot be a non-file URL - sync', testStdioNonFileUrl, 2, execaSync); -test('stdio[*] cannot be a non-file URL - sync', testStdioNonFileUrl, 3, execaSync); - -const testInvalidInputFile = (t, execaMethod) => { - t.throws(() => { - execaMethod('empty.js', getStdio('inputFile', false)); - }, {message: /a file path string or a file URL/}); -}; - -test('inputFile must be a file URL or string', testInvalidInputFile, execa); -test('inputFile must be a file URL or string - sync', testInvalidInputFile, execaSync); - -const testFilePathObject = (t, fdNumber, execaMethod) => { - t.throws(() => { - execaMethod('empty.js', getStdio(fdNumber, foobarString)); - }, {message: /must be used/}); -}; - -test('stdin must be an object when it is a file path string', testFilePathObject, 0, execa); -test('stdout must be an object when it is a file path string', testFilePathObject, 1, execa); -test('stderr must be an object when it is a file path string', testFilePathObject, 2, execa); -test('stdio[*] must be an object when it is a file path string', testFilePathObject, 3, execa); -test('stdin be an object when it is a file path string - sync', testFilePathObject, 0, execaSync); -test('stdout be an object when it is a file path string - sync', testFilePathObject, 1, execaSync); -test('stderr be an object when it is a file path string - sync', testFilePathObject, 2, execaSync); -test('stdio[*] must be an object when it is a file path string - sync', testFilePathObject, 3, execaSync); - -const testFileError = async (t, fixtureName, mapFile, fdNumber) => { - await t.throwsAsync( - execa(fixtureName, [`${fdNumber}`], getStdio(fdNumber, mapFile('./unknown/file'))), - {code: 'ENOENT'}, - ); -}; - -test.serial('inputFile file URL errors should be handled', testFileError, 'stdin-fd.js', pathToFileURL, 'inputFile'); -test.serial('stdin file URL errors should be handled', testFileError, 'stdin-fd.js', pathToFileURL, 0); -test.serial('stdout file URL errors should be handled', testFileError, 'noop-fd.js', pathToFileURL, 1); -test.serial('stderr file URL errors should be handled', testFileError, 'noop-fd.js', pathToFileURL, 2); -test.serial('stdio[*] file URL errors should be handled', testFileError, 'noop-fd.js', pathToFileURL, 3); -test.serial('inputFile file path errors should be handled', testFileError, 'stdin-fd.js', identity, 'inputFile'); -test.serial('stdin file path errors should be handled', testFileError, 'stdin-fd.js', getAbsolutePath, 0); -test.serial('stdout file path errors should be handled', testFileError, 'noop-fd.js', getAbsolutePath, 1); -test.serial('stderr file path errors should be handled', testFileError, 'noop-fd.js', getAbsolutePath, 2); -test.serial('stdio[*] file path errors should be handled', testFileError, 'noop-fd.js', getAbsolutePath, 3); - -const testFileErrorSync = (t, mapFile, fdNumber) => { - t.throws(() => { - execaSync('empty.js', getStdio(fdNumber, mapFile('./unknown/file'))); - }, {code: 'ENOENT'}); -}; - -test('inputFile file URL errors should be handled - sync', testFileErrorSync, pathToFileURL, 'inputFile'); -test('stdin file URL errors should be handled - sync', testFileErrorSync, pathToFileURL, 0); -test('stdout file URL errors should be handled - sync', testFileErrorSync, pathToFileURL, 1); -test('stderr file URL errors should be handled - sync', testFileErrorSync, pathToFileURL, 2); -test('stdio[*] file URL errors should be handled - sync', testFileErrorSync, pathToFileURL, 3); -test('inputFile file path errors should be handled - sync', testFileErrorSync, identity, 'inputFile'); -test('stdin file path errors should be handled - sync', testFileErrorSync, getAbsolutePath, 0); -test('stdout file path errors should be handled - sync', testFileErrorSync, getAbsolutePath, 1); -test('stderr file path errors should be handled - sync', testFileErrorSync, getAbsolutePath, 2); -test('stdio[*] file path errors should be handled - sync', testFileErrorSync, getAbsolutePath, 3); - -const testInputFileObject = async (t, fdNumber, mapFile, execaMethod) => { - const filePath = tempfile(); - await writeFile(filePath, foobarString); - t.throws(() => { - execaMethod('stdin-fd.js', [`${fdNumber}`], getStdio(fdNumber, [ - new Uint8Array(), - mapFile(filePath), - serializeGenerator(true), - ])); - }, {message: /cannot use both files and transforms in objectMode/}); - await rm(filePath); -}; - -test('stdin cannot use objectMode together with input file paths', testInputFileObject, 0, getAbsolutePath, execa); -test('stdin cannot use objectMode together with input file URLs', testInputFileObject, 0, pathToFileURL, execa); -test('stdio[*] cannot use objectMode together with input file paths', testInputFileObject, 3, getAbsolutePath, execa); -test('stdio[*] cannot use objectMode together with input file URLs', testInputFileObject, 3, pathToFileURL, execa); -test('stdin cannot use objectMode together with input file paths, sync', testInputFileObject, 0, getAbsolutePath, execaSync); -test('stdin cannot use objectMode together with input file URLs, sync', testInputFileObject, 0, pathToFileURL, execaSync); - -const testOutputFileObject = async (t, fdNumber, mapFile, execaMethod) => { - const filePath = tempfile(); - t.throws(() => { - execaMethod('noop-fd.js', [`${fdNumber}`, foobarString], getStdio(fdNumber, [ - outputObjectGenerator(), - mapFile(filePath), - ])); - }, {message: /cannot use both files and transforms in objectMode/}); - t.false(await pathExists(filePath)); -}; - -test('stdout cannot use objectMode together with output file paths', testOutputFileObject, 1, getAbsolutePath, execa); -test('stdout cannot use objectMode together with output file URLs', testOutputFileObject, 1, pathToFileURL, execa); -test('stderr cannot use objectMode together with output file paths', testOutputFileObject, 2, getAbsolutePath, execa); -test('stderr cannot use objectMode together with output file URLs', testOutputFileObject, 2, pathToFileURL, execa); -test('stdio[*] cannot use objectMode together with output file paths', testOutputFileObject, 3, getAbsolutePath, execa); -test('stdio[*] cannot use objectMode together with output file URLs', testOutputFileObject, 3, pathToFileURL, execa); -test('stdout cannot use objectMode together with output file paths, sync', testOutputFileObject, 1, getAbsolutePath, execaSync); -test('stdout cannot use objectMode together with output file URLs, sync', testOutputFileObject, 1, pathToFileURL, execaSync); -test('stderr cannot use objectMode together with output file paths, sync', testOutputFileObject, 2, getAbsolutePath, execaSync); -test('stderr cannot use objectMode together with output file URLs, sync', testOutputFileObject, 2, pathToFileURL, execaSync); -test('stdio[*] cannot use objectMode together with output file paths, sync', testOutputFileObject, 3, getAbsolutePath, execaSync); -test('stdio[*] cannot use objectMode together with output file URLs, sync', testOutputFileObject, 3, pathToFileURL, execaSync); - -test('Generator error stops writing to output file', async t => { - const filePath = tempfile(); - const cause = new Error(foobarString); - const error = await t.throwsAsync(execa('noop.js', { - stdout: [throwingGenerator(cause)(), getAbsolutePath(filePath)], - })); - t.is(error.cause, cause); - t.is(await readFile(filePath, 'utf8'), ''); -}); - -test('Generator error does not create output file, sync', async t => { - const filePath = tempfile(); - const cause = new Error(foobarString); - const error = t.throws(() => { - execaSync('noop.js', { - stdout: [throwingGenerator(cause)(), getAbsolutePath(filePath)], - }); - }); - t.is(error.cause, cause); - t.false(await pathExists(filePath)); -}); - -test('Output file error still returns transformed output, sync', async t => { - const filePath = tempfile(); - const {stdout} = t.throws(() => { - execaSync('noop-fd.js', ['1', foobarString], { - stdout: [uppercaseGenerator(), getAbsolutePath('./unknown/file')], - }); - }, {code: 'ENOENT'}); - t.false(await pathExists(filePath)); - t.is(stdout, foobarUppercase); -}); diff --git a/test/stdio/file-path-main.js b/test/stdio/file-path-main.js deleted file mode 100644 index d6987893e2..0000000000 --- a/test/stdio/file-path-main.js +++ /dev/null @@ -1,160 +0,0 @@ -import {readFile, writeFile, rm} from 'node:fs/promises'; -import path from 'node:path'; -import process from 'node:process'; -import {pathToFileURL} from 'node:url'; -import test from 'ava'; -import tempfile from 'tempfile'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {identity, getStdio} from '../helpers/stdio.js'; -import { - runExeca, - runExecaSync, - runScript, - runScriptSync, -} from '../helpers/run.js'; -import {foobarString, foobarUint8Array} from '../helpers/input.js'; -import {getAbsolutePath, getRelativePath} from '../helpers/file-path.js'; - -setFixtureDirectory(); - -const getStdioInput = (fdNumberOrName, file) => { - if (fdNumberOrName === 'string') { - return {input: foobarString}; - } - - if (fdNumberOrName === 'binary') { - return {input: foobarUint8Array}; - } - - return getStdioInputFile(fdNumberOrName, file); -}; - -const getStdioInputFile = (fdNumberOrName, file) => getStdio(fdNumberOrName, typeof fdNumberOrName === 'string' ? file : {file}); - -const testStdinFile = async (t, mapFilePath, fdNumber, execaMethod) => { - const filePath = tempfile(); - await writeFile(filePath, foobarString); - const {stdout} = await execaMethod('stdin.js', getStdio(fdNumber, mapFilePath(filePath))); - t.is(stdout, foobarString); - await rm(filePath); -}; - -test('inputFile can be a file URL', testStdinFile, pathToFileURL, 'inputFile', execa); -test('stdin can be a file URL', testStdinFile, pathToFileURL, 0, execa); -test('inputFile can be an absolute file path', testStdinFile, identity, 'inputFile', execa); -test('stdin can be an absolute file path', testStdinFile, getAbsolutePath, 0, execa); -test('inputFile can be a relative file path', testStdinFile, identity, 'inputFile', execa); -test('stdin can be a relative file path', testStdinFile, getRelativePath, 0, execa); -test('inputFile can be a file URL - sync', testStdinFile, pathToFileURL, 'inputFile', execaSync); -test('stdin can be a file URL - sync', testStdinFile, pathToFileURL, 0, execaSync); -test('inputFile can be an absolute file path - sync', testStdinFile, identity, 'inputFile', execaSync); -test('stdin can be an absolute file path - sync', testStdinFile, getAbsolutePath, 0, execaSync); -test('inputFile can be a relative file path - sync', testStdinFile, identity, 'inputFile', execaSync); -test('stdin can be a relative file path - sync', testStdinFile, getRelativePath, 0, execaSync); - -const testOutputFile = async (t, mapFile, fdNumber, execaMethod) => { - const filePath = tempfile(); - await execaMethod('noop-fd.js', [`${fdNumber}`, foobarString], getStdio(fdNumber, mapFile(filePath))); - t.is(await readFile(filePath, 'utf8'), foobarString); - await rm(filePath); -}; - -test('stdout can be a file URL', testOutputFile, pathToFileURL, 1, execa); -test('stderr can be a file URL', testOutputFile, pathToFileURL, 2, execa); -test('stdio[*] can be a file URL', testOutputFile, pathToFileURL, 3, execa); -test('stdout can be an absolute file path', testOutputFile, getAbsolutePath, 1, execa); -test('stderr can be an absolute file path', testOutputFile, getAbsolutePath, 2, execa); -test('stdio[*] can be an absolute file path', testOutputFile, getAbsolutePath, 3, execa); -test('stdout can be a relative file path', testOutputFile, getRelativePath, 1, execa); -test('stderr can be a relative file path', testOutputFile, getRelativePath, 2, execa); -test('stdio[*] can be a relative file path', testOutputFile, getRelativePath, 3, execa); -test('stdout can be a file URL - sync', testOutputFile, pathToFileURL, 1, execaSync); -test('stderr can be a file URL - sync', testOutputFile, pathToFileURL, 2, execaSync); -test('stdio[*] can be a file URL - sync', testOutputFile, pathToFileURL, 3, execaSync); -test('stdout can be an absolute file path - sync', testOutputFile, getAbsolutePath, 1, execaSync); -test('stderr can be an absolute file path - sync', testOutputFile, getAbsolutePath, 2, execaSync); -test('stdio[*] can be an absolute file path - sync', testOutputFile, getAbsolutePath, 3, execaSync); -test('stdout can be a relative file path - sync', testOutputFile, getRelativePath, 1, execaSync); -test('stderr can be a relative file path - sync', testOutputFile, getRelativePath, 2, execaSync); -test('stdio[*] can be a relative file path - sync', testOutputFile, getRelativePath, 3, execaSync); - -const testInputFileValidUrl = async (t, fdNumber, execaMethod) => { - const filePath = tempfile(); - await writeFile(filePath, foobarString); - const currentCwd = process.cwd(); - process.chdir(path.dirname(filePath)); - - try { - const {stdout} = await execaMethod('stdin.js', getStdioInputFile(fdNumber, path.basename(filePath))); - t.is(stdout, foobarString); - } finally { - process.chdir(currentCwd); - await rm(filePath); - } -}; - -test.serial('inputFile does not need to start with . when being a relative file path', testInputFileValidUrl, 'inputFile', execa); -test.serial('stdin does not need to start with . when being a relative file path', testInputFileValidUrl, 0, execa); -test.serial('inputFile does not need to start with . when being a relative file path - sync', testInputFileValidUrl, 'inputFile', execaSync); -test.serial('stdin does not need to start with . when being a relative file path - sync', testInputFileValidUrl, 0, execaSync); - -const testMultipleInputs = async (t, indices, execaMethod) => { - const filePath = tempfile(); - await writeFile(filePath, foobarString); - const options = Object.assign({}, ...indices.map(fdNumber => getStdioInput(fdNumber, filePath))); - const {stdout} = await execaMethod('stdin.js', options); - t.is(stdout, foobarString.repeat(indices.length)); - await rm(filePath); -}; - -test('inputFile can be set', testMultipleInputs, ['inputFile'], runExeca); -test('inputFile can be set - sync', testMultipleInputs, ['inputFile'], runExecaSync); -test('inputFile can be set with $', testMultipleInputs, ['inputFile'], runScript); -test('inputFile can be set with $.sync', testMultipleInputs, ['inputFile'], runScriptSync); -test('input String and inputFile can be both set', testMultipleInputs, ['inputFile', 'string'], execa); -test('input String and stdin can be both set', testMultipleInputs, [0, 'string'], execa); -test('input Uint8Array and inputFile can be both set', testMultipleInputs, ['inputFile', 'binary'], execa); -test('input Uint8Array and stdin can be both set', testMultipleInputs, [0, 'binary'], execa); -test('stdin and inputFile can be both set', testMultipleInputs, [0, 'inputFile'], execa); -test('input String, stdin and inputFile can be all set', testMultipleInputs, ['inputFile', 0, 'string'], execa); -test('input Uint8Array, stdin and inputFile can be all set', testMultipleInputs, ['inputFile', 0, 'binary'], execa); -test('input String and inputFile can be both set - sync', testMultipleInputs, ['inputFile', 'string'], execaSync); -test('input String and stdin can be both set - sync', testMultipleInputs, [0, 'string'], execaSync); -test('input Uint8Array and inputFile can be both set - sync', testMultipleInputs, ['inputFile', 'binary'], execaSync); -test('input Uint8Array and stdin can be both set - sync', testMultipleInputs, [0, 'binary'], execaSync); -test('stdin and inputFile can be both set - sync', testMultipleInputs, [0, 'inputFile'], execaSync); -test('input String, stdin and inputFile can be all set - sync', testMultipleInputs, ['inputFile', 0, 'string'], execaSync); -test('input Uint8Array, stdin and inputFile can be all set - sync', testMultipleInputs, ['inputFile', 0, 'binary'], execaSync); - -const testMultipleOutputs = async (t, mapFile, fdNumber, execaMethod) => { - const filePath = tempfile(); - const filePathTwo = tempfile(); - await execaMethod('noop-fd.js', [`${fdNumber}`, foobarString], getStdio(fdNumber, [mapFile(filePath), mapFile(filePathTwo)])); - t.is(await readFile(filePath, 'utf8'), foobarString); - t.is(await readFile(filePathTwo, 'utf8'), foobarString); - await Promise.all([rm(filePath), rm(filePathTwo)]); -}; - -test('stdout can be two file URLs', testMultipleOutputs, pathToFileURL, 1, execa); -test('stdout can be two file paths', testMultipleOutputs, getAbsolutePath, 1, execa); -test('stdout can be two file URLs - sync', testMultipleOutputs, pathToFileURL, 1, execaSync); -test('stdout can be two file paths - sync', testMultipleOutputs, getAbsolutePath, 1, execaSync); -test('stderr can be two file URLs', testMultipleOutputs, pathToFileURL, 2, execa); -test('stderr can be two file paths', testMultipleOutputs, getAbsolutePath, 2, execa); -test('stderr can be two file URLs - sync', testMultipleOutputs, pathToFileURL, 2, execaSync); -test('stderr can be two file paths - sync', testMultipleOutputs, getAbsolutePath, 2, execaSync); -test('stdio[*] can be two file URLs', testMultipleOutputs, pathToFileURL, 3, execa); -test('stdio[*] can be two file paths', testMultipleOutputs, getAbsolutePath, 3, execa); -test('stdio[*] can be two file URLs - sync', testMultipleOutputs, pathToFileURL, 3, execaSync); -test('stdio[*] can be two file paths - sync', testMultipleOutputs, getAbsolutePath, 3, execaSync); - -const testInputFileHanging = async (t, mapFilePath) => { - const filePath = tempfile(); - await writeFile(filePath, foobarString); - await t.throwsAsync(execa('stdin.js', {stdin: mapFilePath(filePath), timeout: 1}), {message: /timed out/}); - await rm(filePath); -}; - -test('Passing an input file path when subprocess exits does not make promise hang', testInputFileHanging, getAbsolutePath); -test('Passing an input file URL when subprocess exits does not make promise hang', testInputFileHanging, pathToFileURL); diff --git a/test/stdio/file-path-mixed.js b/test/stdio/file-path-mixed.js deleted file mode 100644 index 0aad81ecef..0000000000 --- a/test/stdio/file-path-mixed.js +++ /dev/null @@ -1,94 +0,0 @@ -import {readFile, writeFile, rm} from 'node:fs/promises'; -import {pathToFileURL} from 'node:url'; -import test from 'ava'; -import tempfile from 'tempfile'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {getStdio} from '../helpers/stdio.js'; -import {foobarString, foobarUppercase} from '../helpers/input.js'; -import {uppercaseGenerator} from '../helpers/generator.js'; -import {getAbsolutePath} from '../helpers/file-path.js'; - -setFixtureDirectory(); - -const testInputFileTransform = async (t, fdNumber, mapFile, execaMethod) => { - const filePath = tempfile(); - await writeFile(filePath, foobarString); - const {stdout} = await execaMethod('stdin-fd.js', [`${fdNumber}`], getStdio(fdNumber, [ - new Uint8Array(), - mapFile(filePath), - uppercaseGenerator(), - ])); - t.is(stdout, foobarUppercase); - await rm(filePath); -}; - -test('stdin can use generators together with input file paths', testInputFileTransform, 0, getAbsolutePath, execa); -test('stdin can use generators together with input file URLs', testInputFileTransform, 0, pathToFileURL, execa); -test('stdio[*] can use generators together with input file paths', testInputFileTransform, 3, getAbsolutePath, execa); -test('stdio[*] can use generators together with input file URLs', testInputFileTransform, 3, pathToFileURL, execa); -test('stdin can use generators together with input file paths, sync', testInputFileTransform, 0, getAbsolutePath, execaSync); -test('stdin can use generators together with input file URLs, sync', testInputFileTransform, 0, pathToFileURL, execaSync); - -const testOutputFileTransform = async (t, fdNumber, mapFile, execaMethod) => { - const filePath = tempfile(); - await execaMethod('noop-fd.js', [`${fdNumber}`, foobarString], getStdio(fdNumber, [ - uppercaseGenerator(), - mapFile(filePath), - ])); - t.is(await readFile(filePath, 'utf8'), `${foobarUppercase}\n`); - await rm(filePath); -}; - -test('stdout can use generators together with output file paths', testOutputFileTransform, 1, getAbsolutePath, execa); -test('stdout can use generators together with output file URLs', testOutputFileTransform, 1, pathToFileURL, execa); -test('stderr can use generators together with output file paths', testOutputFileTransform, 2, getAbsolutePath, execa); -test('stderr can use generators together with output file URLs', testOutputFileTransform, 2, pathToFileURL, execa); -test('stdio[*] can use generators together with output file paths', testOutputFileTransform, 3, getAbsolutePath, execa); -test('stdio[*] can use generators together with output file URLs', testOutputFileTransform, 3, pathToFileURL, execa); -test('stdout can use generators together with output file paths, sync', testOutputFileTransform, 1, getAbsolutePath, execaSync); -test('stdout can use generators together with output file URLs, sync', testOutputFileTransform, 1, pathToFileURL, execaSync); -test('stderr can use generators together with output file paths, sync', testOutputFileTransform, 2, getAbsolutePath, execaSync); -test('stderr can use generators together with output file URLs, sync', testOutputFileTransform, 2, pathToFileURL, execaSync); -test('stdio[*] can use generators together with output file paths, sync', testOutputFileTransform, 3, getAbsolutePath, execaSync); -test('stdio[*] can use generators together with output file URLs, sync', testOutputFileTransform, 3, pathToFileURL, execaSync); - -const testOutputFileLines = async (t, fdNumber, mapFile, execaMethod) => { - const filePath = tempfile(); - const {stdio} = await execaMethod('noop-fd.js', [`${fdNumber}`, foobarString], { - ...getStdio(fdNumber, mapFile(filePath)), - lines: true, - }); - t.deepEqual(stdio[fdNumber], [foobarString]); - t.is(await readFile(filePath, 'utf8'), foobarString); - await rm(filePath); -}; - -test('stdout can use "lines: true" together with output file paths', testOutputFileLines, 1, getAbsolutePath, execa); -test('stdout can use "lines: true" together with output file URLs', testOutputFileLines, 1, pathToFileURL, execa); -test('stderr can use "lines: true" together with output file paths', testOutputFileLines, 2, getAbsolutePath, execa); -test('stderr can use "lines: true" together with output file URLs', testOutputFileLines, 2, pathToFileURL, execa); -test('stdio[*] can use "lines: true" together with output file paths', testOutputFileLines, 3, getAbsolutePath, execa); -test('stdio[*] can use "lines: true" together with output file URLs', testOutputFileLines, 3, pathToFileURL, execa); -test('stdout can use "lines: true" together with output file paths, sync', testOutputFileLines, 1, getAbsolutePath, execaSync); -test('stdout can use "lines: true" together with output file URLs, sync', testOutputFileLines, 1, pathToFileURL, execaSync); -test('stderr can use "lines: true" together with output file paths, sync', testOutputFileLines, 2, getAbsolutePath, execaSync); -test('stderr can use "lines: true" together with output file URLs, sync', testOutputFileLines, 2, pathToFileURL, execaSync); -test('stdio[*] can use "lines: true" together with output file paths, sync', testOutputFileLines, 3, getAbsolutePath, execaSync); -test('stdio[*] can use "lines: true" together with output file URLs, sync', testOutputFileLines, 3, pathToFileURL, execaSync); - -const testOutputFileNoBuffer = async (t, buffer, execaMethod) => { - const filePath = tempfile(); - const {stdout} = await execaMethod('noop-fd.js', ['1', foobarString], { - stdout: getAbsolutePath(filePath), - buffer, - }); - t.is(stdout, undefined); - t.is(await readFile(filePath, 'utf8'), foobarString); - await rm(filePath); -}; - -test('stdout can use "buffer: false" together with output file paths', testOutputFileNoBuffer, false, execa); -test('stdout can use "buffer: false" together with output file paths, fd-specific', testOutputFileNoBuffer, {stdout: false}, execa); -test('stdout can use "buffer: false" together with output file paths, sync', testOutputFileNoBuffer, false, execaSync); -test('stdout can use "buffer: false" together with output file paths, fd-specific, sync', testOutputFileNoBuffer, {stdout: false}, execaSync); diff --git a/test/stdio/handle-invalid.js b/test/stdio/handle-invalid.js deleted file mode 100644 index 6e82caa585..0000000000 --- a/test/stdio/handle-invalid.js +++ /dev/null @@ -1,60 +0,0 @@ -import test from 'ava'; -import {execa, execaSync} from '../../index.js'; -import {getStdio} from '../helpers/stdio.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; - -setFixtureDirectory(); - -const testEmptyArray = (t, fdNumber, optionName, execaMethod) => { - t.throws(() => { - execaMethod('empty.js', getStdio(fdNumber, [])); - }, {message: `The \`${optionName}\` option must not be an empty array.`}); -}; - -test('Cannot pass an empty array to stdin', testEmptyArray, 0, 'stdin', execa); -test('Cannot pass an empty array to stdout', testEmptyArray, 1, 'stdout', execa); -test('Cannot pass an empty array to stderr', testEmptyArray, 2, 'stderr', execa); -test('Cannot pass an empty array to stdio[*]', testEmptyArray, 3, 'stdio[3]', execa); -test('Cannot pass an empty array to stdin - sync', testEmptyArray, 0, 'stdin', execaSync); -test('Cannot pass an empty array to stdout - sync', testEmptyArray, 1, 'stdout', execaSync); -test('Cannot pass an empty array to stderr - sync', testEmptyArray, 2, 'stderr', execaSync); -test('Cannot pass an empty array to stdio[*] - sync', testEmptyArray, 3, 'stdio[3]', execaSync); - -const testInvalidValueSync = (t, fdNumber, stdioOption) => { - const {message} = t.throws(() => { - execaSync('empty.js', getStdio(fdNumber, stdioOption)); - }); - t.true(message.includes(`cannot be "${stdioOption}" with synchronous methods`)); -}; - -test('stdin cannot be "ipc", sync', testInvalidValueSync, 0, 'ipc'); -test('stdout cannot be "ipc", sync', testInvalidValueSync, 1, 'ipc'); -test('stderr cannot be "ipc", sync', testInvalidValueSync, 2, 'ipc'); -test('stdio[*] cannot be "ipc", sync', testInvalidValueSync, 3, 'ipc'); -test('stdin cannot be "overlapped", sync', testInvalidValueSync, 0, 'overlapped'); -test('stdout cannot be "overlapped", sync', testInvalidValueSync, 1, 'overlapped'); -test('stderr cannot be "overlapped", sync', testInvalidValueSync, 2, 'overlapped'); -test('stdio[*] cannot be "overlapped", sync', testInvalidValueSync, 3, 'overlapped'); - -const testInvalidArrayValue = (t, invalidStdio, fdNumber, execaMethod) => { - t.throws(() => { - execaMethod('empty.js', getStdio(fdNumber, ['pipe', invalidStdio])); - }, {message: /must not include/}); -}; - -test('Cannot pass "ignore" and another value to stdin', testInvalidArrayValue, 'ignore', 0, execa); -test('Cannot pass "ignore" and another value to stdout', testInvalidArrayValue, 'ignore', 1, execa); -test('Cannot pass "ignore" and another value to stderr', testInvalidArrayValue, 'ignore', 2, execa); -test('Cannot pass "ignore" and another value to stdio[*]', testInvalidArrayValue, 'ignore', 3, execa); -test('Cannot pass "ignore" and another value to stdin - sync', testInvalidArrayValue, 'ignore', 0, execaSync); -test('Cannot pass "ignore" and another value to stdout - sync', testInvalidArrayValue, 'ignore', 1, execaSync); -test('Cannot pass "ignore" and another value to stderr - sync', testInvalidArrayValue, 'ignore', 2, execaSync); -test('Cannot pass "ignore" and another value to stdio[*] - sync', testInvalidArrayValue, 'ignore', 3, execaSync); -test('Cannot pass "ipc" and another value to stdin', testInvalidArrayValue, 'ipc', 0, execa); -test('Cannot pass "ipc" and another value to stdout', testInvalidArrayValue, 'ipc', 1, execa); -test('Cannot pass "ipc" and another value to stderr', testInvalidArrayValue, 'ipc', 2, execa); -test('Cannot pass "ipc" and another value to stdio[*]', testInvalidArrayValue, 'ipc', 3, execa); -test('Cannot pass "ipc" and another value to stdin - sync', testInvalidArrayValue, 'ipc', 0, execaSync); -test('Cannot pass "ipc" and another value to stdout - sync', testInvalidArrayValue, 'ipc', 1, execaSync); -test('Cannot pass "ipc" and another value to stderr - sync', testInvalidArrayValue, 'ipc', 2, execaSync); -test('Cannot pass "ipc" and another value to stdio[*] - sync', testInvalidArrayValue, 'ipc', 3, execaSync); diff --git a/test/stdio/handle-normal.js b/test/stdio/handle-normal.js deleted file mode 100644 index a906e54303..0000000000 --- a/test/stdio/handle-normal.js +++ /dev/null @@ -1,47 +0,0 @@ -import test from 'ava'; -import {execa} from '../../index.js'; -import {getStdio} from '../helpers/stdio.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString, foobarUint8Array} from '../helpers/input.js'; - -setFixtureDirectory(); - -const testInputOverlapped = async (t, fdNumber) => { - const {stdout} = await execa('stdin-fd.js', [`${fdNumber}`, foobarString], getStdio(fdNumber, [foobarUint8Array, 'overlapped', 'pipe'])); - t.is(stdout, foobarString); -}; - -test('stdin can be ["overlapped", "pipe"]', testInputOverlapped, 0); -test('stdio[*] input can be ["overlapped", "pipe"]', testInputOverlapped, 3); - -const testOutputOverlapped = async (t, fdNumber) => { - const {stdio} = await execa('noop-fd.js', [`${fdNumber}`, foobarString], getStdio(fdNumber, ['overlapped', 'pipe'])); - t.is(stdio[fdNumber], foobarString); -}; - -test('stdout can be ["overlapped", "pipe"]', testOutputOverlapped, 1); -test('stderr can be ["overlapped", "pipe"]', testOutputOverlapped, 2); -test('stdio[*] output can be ["overlapped", "pipe"]', testOutputOverlapped, 3); - -const testFd3Undefined = async (t, stdioOption, options) => { - const subprocess = execa('empty.js', {...getStdio(3, stdioOption), ...options}); - t.is(subprocess.stdio.length, 4); - t.is(subprocess.stdio[3], null); - - const {stdio} = await subprocess; - t.is(stdio.length, 4); - t.is(stdio[3], undefined); -}; - -test('stdio[*] undefined means "ignore"', testFd3Undefined, undefined, {}); -test('stdio[*] null means "ignore"', testFd3Undefined, null, {}); -test('stdio[*] [undefined] means "ignore"', testFd3Undefined, [undefined], {}); -test('stdio[*] [null] means "ignore"', testFd3Undefined, [null], {}); -test('stdio[*] undefined means "ignore", "lines: true"', testFd3Undefined, undefined, {lines: true}); -test('stdio[*] null means "ignore", "lines: true"', testFd3Undefined, null, {lines: true}); -test('stdio[*] [undefined] means "ignore", "lines: true"', testFd3Undefined, [undefined], {lines: true}); -test('stdio[*] [null] means "ignore", "lines: true"', testFd3Undefined, [null], {lines: true}); -test('stdio[*] undefined means "ignore", "encoding: hex"', testFd3Undefined, undefined, {encoding: 'hex'}); -test('stdio[*] null means "ignore", "encoding: hex"', testFd3Undefined, null, {encoding: 'hex'}); -test('stdio[*] [undefined] means "ignore", "encoding: hex"', testFd3Undefined, [undefined], {encoding: 'hex'}); -test('stdio[*] [null] means "ignore", "encoding: hex"', testFd3Undefined, [null], {encoding: 'hex'}); diff --git a/test/stdio/handle-options.js b/test/stdio/handle-options.js deleted file mode 100644 index 487c764904..0000000000 --- a/test/stdio/handle-options.js +++ /dev/null @@ -1,49 +0,0 @@ -import test from 'ava'; -import {execa} from '../../index.js'; -import {getStdio} from '../helpers/stdio.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; - -setFixtureDirectory(); - -const testNoPipeOption = async (t, stdioOption, fdNumber) => { - const subprocess = execa('empty.js', getStdio(fdNumber, stdioOption)); - t.is(subprocess.stdio[fdNumber], null); - await subprocess; -}; - -test('stdin can be "ignore"', testNoPipeOption, 'ignore', 0); -test('stdin can be ["ignore"]', testNoPipeOption, ['ignore'], 0); -test('stdin can be ["ignore", "ignore"]', testNoPipeOption, ['ignore', 'ignore'], 0); -test('stdin can be "ipc"', testNoPipeOption, 'ipc', 0); -test('stdin can be ["ipc"]', testNoPipeOption, ['ipc'], 0); -test('stdin can be "inherit"', testNoPipeOption, 'inherit', 0); -test('stdin can be ["inherit"]', testNoPipeOption, ['inherit'], 0); -test('stdin can be 0', testNoPipeOption, 0, 0); -test('stdin can be [0]', testNoPipeOption, [0], 0); -test('stdout can be "ignore"', testNoPipeOption, 'ignore', 1); -test('stdout can be ["ignore"]', testNoPipeOption, ['ignore'], 1); -test('stdout can be ["ignore", "ignore"]', testNoPipeOption, ['ignore', 'ignore'], 1); -test('stdout can be "ipc"', testNoPipeOption, 'ipc', 1); -test('stdout can be ["ipc"]', testNoPipeOption, ['ipc'], 1); -test('stdout can be "inherit"', testNoPipeOption, 'inherit', 1); -test('stdout can be ["inherit"]', testNoPipeOption, ['inherit'], 1); -test('stdout can be 1', testNoPipeOption, 1, 1); -test('stdout can be [1]', testNoPipeOption, [1], 1); -test('stderr can be "ignore"', testNoPipeOption, 'ignore', 2); -test('stderr can be ["ignore"]', testNoPipeOption, ['ignore'], 2); -test('stderr can be ["ignore", "ignore"]', testNoPipeOption, ['ignore', 'ignore'], 2); -test('stderr can be "ipc"', testNoPipeOption, 'ipc', 2); -test('stderr can be ["ipc"]', testNoPipeOption, ['ipc'], 2); -test('stderr can be "inherit"', testNoPipeOption, 'inherit', 2); -test('stderr can be ["inherit"]', testNoPipeOption, ['inherit'], 2); -test('stderr can be 2', testNoPipeOption, 2, 2); -test('stderr can be [2]', testNoPipeOption, [2], 2); -test('stdio[*] can be "ignore"', testNoPipeOption, 'ignore', 3); -test('stdio[*] can be ["ignore"]', testNoPipeOption, ['ignore'], 3); -test('stdio[*] can be ["ignore", "ignore"]', testNoPipeOption, ['ignore', 'ignore'], 3); -test('stdio[*] can be "ipc"', testNoPipeOption, 'ipc', 3); -test('stdio[*] can be ["ipc"]', testNoPipeOption, ['ipc'], 3); -test('stdio[*] can be "inherit"', testNoPipeOption, 'inherit', 3); -test('stdio[*] can be ["inherit"]', testNoPipeOption, ['inherit'], 3); -test('stdio[*] can be 3', testNoPipeOption, 3, 3); -test('stdio[*] can be [3]', testNoPipeOption, [3], 3); diff --git a/test/stdio/iterable.js b/test/stdio/iterable.js deleted file mode 100644 index 7799f94a3f..0000000000 --- a/test/stdio/iterable.js +++ /dev/null @@ -1,202 +0,0 @@ -import {once} from 'node:events'; -import {setImmediate} from 'node:timers/promises'; -import test from 'ava'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {getStdio} from '../helpers/stdio.js'; -import { - foobarString, - foobarObject, - foobarObjectString, - foobarArray, -} from '../helpers/input.js'; -import { - noopGenerator, - serializeGenerator, - infiniteGenerator, - throwingGenerator, -} from '../helpers/generator.js'; - -const stringGenerator = function * () { - yield * foobarArray; -}; - -const textEncoder = new TextEncoder(); -const binaryFoo = textEncoder.encode('foo'); -const binaryBar = textEncoder.encode('bar'); -const binaryArray = [binaryFoo, binaryBar]; - -const binaryGenerator = function * () { - yield * binaryArray; -}; - -const mixedArray = [foobarArray[0], binaryArray[1]]; - -const mixedGenerator = function * () { - yield * mixedArray; -}; - -const asyncGenerator = async function * () { - await setImmediate(); - yield * foobarArray; -}; - -setFixtureDirectory(); - -const testIterable = async (t, stdioOption, fdNumber, execaMethod) => { - const {stdout} = await execaMethod('stdin-fd.js', [`${fdNumber}`], getStdio(fdNumber, stdioOption)); - t.is(stdout, 'foobar'); -}; - -test.serial('stdin option can be an array of strings', testIterable, [foobarArray], 0, execa); -test.serial('stdin option can be an array of strings - sync', testIterable, [foobarArray], 0, execaSync); -test.serial('stdio[*] option can be an array of strings', testIterable, [foobarArray], 3, execa); -test.serial('stdin option can be an array of Uint8Arrays', testIterable, [binaryArray], 0, execa); -test.serial('stdin option can be an array of Uint8Arrays - sync', testIterable, [binaryArray], 0, execaSync); -test.serial('stdio[*] option can be an array of Uint8Arrays', testIterable, [binaryArray], 3, execa); -test.serial('stdin option can be an iterable of strings', testIterable, stringGenerator(), 0, execa); -test.serial('stdin option can be an iterable of strings - sync', testIterable, stringGenerator(), 0, execaSync); -test.serial('stdio[*] option can be an iterable of strings', testIterable, stringGenerator(), 3, execa); -test.serial('stdin option can be an iterable of Uint8Arrays', testIterable, binaryGenerator(), 0, execa); -test.serial('stdin option can be an iterable of Uint8Arrays - sync', testIterable, binaryGenerator(), 0, execaSync); -test.serial('stdio[*] option can be an iterable of Uint8Arrays', testIterable, binaryGenerator(), 3, execa); -test.serial('stdin option can be an iterable of strings + Uint8Arrays', testIterable, mixedGenerator(), 0, execa); -test.serial('stdin option can be an iterable of strings + Uint8Arrays - sync', testIterable, mixedGenerator(), 0, execaSync); -test.serial('stdio[*] option can be an iterable of strings + Uint8Arrays', testIterable, mixedGenerator(), 3, execa); -test.serial('stdin option can be an async iterable', testIterable, asyncGenerator(), 0, execa); -test.serial('stdio[*] option can be an async iterable', testIterable, asyncGenerator(), 3, execa); - -const foobarObjectGenerator = function * () { - yield foobarObject; -}; - -const foobarAsyncObjectGenerator = async function * () { - yield foobarObject; -}; - -const testObjectIterable = async (t, stdioOption, fdNumber, execaMethod) => { - const {stdout} = await execaMethod('stdin-fd.js', [`${fdNumber}`], getStdio(fdNumber, [stdioOption, serializeGenerator(true)])); - t.is(stdout, foobarObjectString); -}; - -test('stdin option can be an array of objects', testObjectIterable, [foobarObject], 0, execa); -test('stdio[*] option can be an array of objects', testObjectIterable, [foobarObject], 3, execa); -test('stdin option can be an iterable of objects', testObjectIterable, foobarObjectGenerator(), 0, execa); -test('stdio[*] option can be an iterable of objects', testObjectIterable, foobarObjectGenerator(), 3, execa); -test('stdin option can be an async iterable of objects', testObjectIterable, foobarAsyncObjectGenerator(), 0, execa); -test('stdio[*] option can be an async iterable of objects', testObjectIterable, foobarAsyncObjectGenerator(), 3, execa); -test('stdin option can be an array of objects - sync', testObjectIterable, [foobarObject], 0, execaSync); -test('stdin option can be an iterable of objects - sync', testObjectIterable, foobarObjectGenerator(), 0, execaSync); - -const testIterableNoGeneratorsSync = (t, stdioOption, fdNumber) => { - t.throws(() => { - execaSync('empty.js', getStdio(fdNumber, stdioOption)); - }, {message: /must be used to serialize/}); -}; - -test('stdin option cannot be an array of objects without generators - sync', testIterableNoGeneratorsSync, [[foobarObject]], 0); -test('stdin option cannot be an iterable of objects without generators - sync', testIterableNoGeneratorsSync, foobarObjectGenerator(), 0); - -const testIterableNoSerializeSync = (t, stdioOption, fdNumber) => { - t.throws(() => { - execaSync('empty.js', getStdio(fdNumber, [stdioOption, noopGenerator()])); - }, {message: /The `stdin` option's transform must use "objectMode: true" to receive as input: object/}); -}; - -test('stdin option cannot be an array of objects without serializing - sync', testIterableNoSerializeSync, [foobarObject], 0); -test('stdin option cannot be an iterable of objects without serializing - sync', testIterableNoSerializeSync, foobarObjectGenerator(), 0); - -const testIterableSync = (t, stdioOption, fdNumber) => { - t.throws(() => { - execaSync('empty.js', getStdio(fdNumber, stdioOption)); - }, {message: /an iterable with synchronous methods/}); -}; - -test('stdio[*] option cannot be an array of strings - sync', testIterableSync, [foobarArray], 3); -test('stdio[*] option cannot be an array of Uint8Arrays - sync', testIterableSync, [binaryArray], 3); -test('stdio[*] option cannot be an array of objects - sync', testIterableSync, [[foobarObject]], 3); -test('stdio[*] option cannot be an iterable of strings - sync', testIterableSync, stringGenerator(), 3); -test('stdio[*] option cannot be an iterable of Uint8Arrays - sync', testIterableSync, binaryGenerator(), 3); -test('stdio[*] option cannot be an iterable of objects - sync', testIterableSync, foobarObjectGenerator(), 3); -test('stdio[*] option cannot be multiple iterables - sync', testIterableSync, [stringGenerator(), stringGenerator()], 3); - -const testAsyncIterableSync = (t, stdioOption, fdNumber) => { - t.throws(() => { - execaSync('empty.js', getStdio(fdNumber, stdioOption)); - }, {message: /an async iterable with synchronous method/}); -}; - -test('stdin option cannot be an async iterable - sync', testAsyncIterableSync, asyncGenerator(), 0); -test('stdio[*] option cannot be an async iterable - sync', testAsyncIterableSync, asyncGenerator(), 3); -test('stdin option cannot be an async iterable of objects - sync', testAsyncIterableSync, foobarAsyncObjectGenerator(), 0); -test('stdio[*] option cannot be an async iterable of objects - sync', testAsyncIterableSync, foobarAsyncObjectGenerator(), 3); - -const testIterableError = async (t, fdNumber) => { - const cause = new Error(foobarString); - const error = await t.throwsAsync(execa('stdin-fd.js', [`${fdNumber}`], getStdio(fdNumber, throwingGenerator(cause)().transform()))); - t.is(error.cause, cause); -}; - -test('stdin option handles errors in iterables', testIterableError, 0); -test('stdio[*] option handles errors in iterables', testIterableError, 3); - -const testIterableErrorSync = (t, fdNumber) => { - const cause = new Error(foobarString); - const error = t.throws(() => { - execaSync('stdin-fd.js', [`${fdNumber}`], getStdio(fdNumber, throwingGenerator(cause)().transform())); - }); - t.is(error, cause); -}; - -test('stdin option handles errors in iterables - sync', testIterableErrorSync, 0); -test('stdio[*] option handles errors in iterables - sync', testIterableErrorSync, 3); - -const testNoIterableOutput = (t, stdioOption, fdNumber, execaMethod) => { - t.throws(() => { - execaMethod('empty.js', getStdio(fdNumber, stdioOption)); - }, {message: /cannot be an iterable/}); -}; - -test('stdout option cannot be an array of strings', testNoIterableOutput, [foobarArray], 1, execa); -test('stderr option cannot be an array of strings', testNoIterableOutput, [foobarArray], 2, execa); -test('stdout option cannot be an array of strings - sync', testNoIterableOutput, [foobarArray], 1, execaSync); -test('stderr option cannot be an array of strings - sync', testNoIterableOutput, [foobarArray], 2, execaSync); -test('stdout option cannot be an iterable', testNoIterableOutput, stringGenerator(), 1, execa); -test('stderr option cannot be an iterable', testNoIterableOutput, stringGenerator(), 2, execa); -test('stdout option cannot be an iterable - sync', testNoIterableOutput, stringGenerator(), 1, execaSync); -test('stderr option cannot be an iterable - sync', testNoIterableOutput, stringGenerator(), 2, execaSync); - -test('stdin option can be an infinite iterable', async t => { - const iterable = infiniteGenerator().transform(); - const subprocess = execa('stdin.js', getStdio(0, iterable)); - await once(subprocess.stdout, 'data'); - subprocess.kill(); - const {stdout} = await t.throwsAsync(subprocess); - t.true(stdout.startsWith('foo')); - t.deepEqual(await iterable.next(), {value: undefined, done: true}); -}); - -const testMultipleIterable = async (t, stdioOption, fdNumber, execaMethod) => { - const {stdout} = await execaMethod('stdin-fd.js', [`${fdNumber}`], getStdio(fdNumber, stdioOption)); - const expectedOutputs = [ - `${foobarArray[0]}${foobarArray[1]}${foobarArray[0]}${foobarArray[1]}`, - `${foobarArray[0]}${foobarArray[0]}${foobarArray[1]}${foobarArray[1]}`, - ]; - t.true(expectedOutputs.includes(stdout)); -}; - -test('stdin option can be multiple iterables', testMultipleIterable, [stringGenerator(), stringGenerator()], 0, execa); -test('stdio[*] option can be multiple iterables', testMultipleIterable, [stringGenerator(), stringGenerator()], 3, execa); -test('stdin option can be multiple iterables - sync', testMultipleIterable, [stringGenerator(), stringGenerator()], 0, execaSync); -test('stdin option can be multiple mixed iterables', testMultipleIterable, [stringGenerator(), binaryGenerator()], 0, execa); -test('stdio[*] option can be multiple mixed iterables', testMultipleIterable, [stringGenerator(), binaryGenerator()], 3, execa); -test('stdin option can be multiple mixed iterables - sync', testMultipleIterable, [stringGenerator(), binaryGenerator()], 0, execaSync); -test('stdin option can be sync/async mixed iterables', testMultipleIterable, [stringGenerator(), asyncGenerator()], 0, execa); -test('stdio[*] option can be sync/async mixed iterables', testMultipleIterable, [stringGenerator(), asyncGenerator()], 3, execa); - -test('stdin option iterable is canceled on subprocess error', async t => { - const iterable = infiniteGenerator().transform(); - await t.throwsAsync(execa('stdin.js', {stdin: iterable, timeout: 1}), {message: /timed out/}); - // eslint-disable-next-line no-empty - for await (const _ of iterable) {} -}); diff --git a/test/stdio/lines-main.js b/test/stdio/lines-main.js deleted file mode 100644 index f671174639..0000000000 --- a/test/stdio/lines-main.js +++ /dev/null @@ -1,108 +0,0 @@ -import test from 'ava'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {fullStdio} from '../helpers/stdio.js'; -import {getOutputsGenerator} from '../helpers/generator.js'; -import {foobarString} from '../helpers/input.js'; -import { - simpleFull, - simpleChunks, - simpleFullEndChunks, - simpleLines, - simpleFullEndLines, - noNewlinesChunks, - getSimpleChunkSubprocessAsync, -} from '../helpers/lines.js'; - -setFixtureDirectory(); - -// eslint-disable-next-line max-params -const testStreamLines = async (t, fdNumber, input, expectedOutput, lines, stripFinalNewline, execaMethod) => { - const {stdio} = await execaMethod('noop-fd.js', [`${fdNumber}`, input], {...fullStdio, lines, stripFinalNewline}); - t.deepEqual(stdio[fdNumber], expectedOutput); -}; - -test('"lines: true" splits lines, stdout', testStreamLines, 1, simpleFull, simpleLines, true, false, execa); -test('"lines: true" splits lines, stdout, fd-specific', testStreamLines, 1, simpleFull, simpleLines, {stdout: true}, false, execa); -test('"lines: true" splits lines, stderr', testStreamLines, 2, simpleFull, simpleLines, true, false, execa); -test('"lines: true" splits lines, stderr, fd-specific', testStreamLines, 2, simpleFull, simpleLines, {stderr: true}, false, execa); -test('"lines: true" splits lines, stdio[*]', testStreamLines, 3, simpleFull, simpleLines, true, false, execa); -test('"lines: true" splits lines, stdio[*], fd-specific', testStreamLines, 3, simpleFull, simpleLines, {fd3: true}, false, execa); -test('"lines: true" splits lines, stdout, stripFinalNewline', testStreamLines, 1, simpleFull, noNewlinesChunks, true, true, execa); -test('"lines: true" splits lines, stdout, stripFinalNewline, fd-specific', testStreamLines, 1, simpleFull, noNewlinesChunks, true, {stdout: true}, execa); -test('"lines: true" splits lines, stderr, stripFinalNewline', testStreamLines, 2, simpleFull, noNewlinesChunks, true, true, execa); -test('"lines: true" splits lines, stderr, stripFinalNewline, fd-specific', testStreamLines, 2, simpleFull, noNewlinesChunks, true, {stderr: true}, execa); -test('"lines: true" splits lines, stdio[*], stripFinalNewline', testStreamLines, 3, simpleFull, noNewlinesChunks, true, true, execa); -test('"lines: true" splits lines, stdio[*], stripFinalNewline, fd-specific', testStreamLines, 3, simpleFull, noNewlinesChunks, true, {fd3: true}, execa); -test('"lines: true" splits lines, stdout, sync', testStreamLines, 1, simpleFull, simpleLines, true, false, execaSync); -test('"lines: true" splits lines, stdout, fd-specific, sync', testStreamLines, 1, simpleFull, simpleLines, {stdout: true}, false, execaSync); -test('"lines: true" splits lines, stderr, sync', testStreamLines, 2, simpleFull, simpleLines, true, false, execaSync); -test('"lines: true" splits lines, stderr, fd-specific, sync', testStreamLines, 2, simpleFull, simpleLines, {stderr: true}, false, execaSync); -test('"lines: true" splits lines, stdio[*], sync', testStreamLines, 3, simpleFull, simpleLines, true, false, execaSync); -test('"lines: true" splits lines, stdio[*], fd-specific, sync', testStreamLines, 3, simpleFull, simpleLines, {fd3: true}, false, execaSync); -test('"lines: true" splits lines, stdout, stripFinalNewline, sync', testStreamLines, 1, simpleFull, noNewlinesChunks, true, true, execaSync); -test('"lines: true" splits lines, stdout, stripFinalNewline, fd-specific, sync', testStreamLines, 1, simpleFull, noNewlinesChunks, true, {stdout: true}, execaSync); -test('"lines: true" splits lines, stderr, stripFinalNewline, sync', testStreamLines, 2, simpleFull, noNewlinesChunks, true, true, execaSync); -test('"lines: true" splits lines, stderr, stripFinalNewline, fd-specific, sync', testStreamLines, 2, simpleFull, noNewlinesChunks, true, {stderr: true}, execaSync); -test('"lines: true" splits lines, stdio[*], stripFinalNewline, sync', testStreamLines, 3, simpleFull, noNewlinesChunks, true, true, execaSync); -test('"lines: true" splits lines, stdio[*], stripFinalNewline, fd-specific, sync', testStreamLines, 3, simpleFull, noNewlinesChunks, true, {fd3: true}, execaSync); - -const bigArray = Array.from({length: 1e5}).fill('.\n'); -const bigString = bigArray.join(''); -const bigStringNoNewlines = '.'.repeat(1e6); -const bigStringNoNewlinesEnd = `${bigStringNoNewlines}\n`; - -// eslint-disable-next-line max-params -const testStreamLinesGenerator = async (t, input, expectedLines, objectMode, binary, stripFinalNewline, execaMethod) => { - const {stdout} = await execaMethod('noop.js', { - stdout: getOutputsGenerator(input)(objectMode, binary), - lines: true, - stripFinalNewline, - }); - t.deepEqual(stdout, expectedLines); -}; - -test('"lines: true" works with strings generators', testStreamLinesGenerator, simpleChunks, simpleFullEndLines, false, false, false, execa); -test('"lines: true" works with strings generators, binary', testStreamLinesGenerator, simpleChunks, simpleLines, false, true, false, execa); -test('"lines: true" works with big strings generators', testStreamLinesGenerator, [bigString], bigArray, false, false, false, execa); -test('"lines: true" works with big strings generators without newlines', testStreamLinesGenerator, [bigStringNoNewlines], [bigStringNoNewlinesEnd], false, false, false, execa); -test('"lines: true" is a noop with strings generators, objectMode', testStreamLinesGenerator, simpleFullEndChunks, simpleFullEndChunks, true, false, false, execa); -test('"lines: true" is a noop with strings generators, stripFinalNewline, objectMode', testStreamLinesGenerator, simpleFullEndChunks, simpleFullEndChunks, true, false, true, execa); -test('"lines: true" is a noop with strings generators, stripFinalNewline, fd-specific, objectMode', testStreamLinesGenerator, simpleFullEndChunks, simpleFullEndChunks, true, false, {stdout: true}, execa); -test('"lines: true" is a noop with strings generators, binary, objectMode', testStreamLinesGenerator, simpleChunks, simpleChunks, true, true, false, execa); -test('"lines: true" is a noop big strings generators, objectMode', testStreamLinesGenerator, [bigString], [bigString], true, false, false, execa); -test('"lines: true" is a noop big strings generators without newlines, objectMode', testStreamLinesGenerator, [bigStringNoNewlines], [bigStringNoNewlines], true, false, false, execa); -test('"lines: true" works with strings generators, sync', testStreamLinesGenerator, simpleChunks, simpleFullEndLines, false, false, false, execaSync); -test('"lines: true" works with strings generators, binary, sync', testStreamLinesGenerator, simpleChunks, simpleLines, false, true, false, execaSync); -test('"lines: true" works with big strings generators, sync', testStreamLinesGenerator, [bigString], bigArray, false, false, false, execaSync); -test('"lines: true" works with big strings generators without newlines, sync', testStreamLinesGenerator, [bigStringNoNewlines], [bigStringNoNewlinesEnd], false, false, false, execaSync); -test('"lines: true" is a noop with strings generators, objectMode, sync', testStreamLinesGenerator, simpleFullEndChunks, simpleFullEndChunks, true, false, false, execaSync); -test('"lines: true" is a noop with strings generators, stripFinalNewline, objectMode, sync', testStreamLinesGenerator, simpleFullEndChunks, simpleFullEndChunks, true, false, true, execaSync); -test('"lines: true" is a noop with strings generators, stripFinalNewline, fd-specific, objectMode, sync', testStreamLinesGenerator, simpleFullEndChunks, simpleFullEndChunks, true, false, {stdout: true}, execaSync); -test('"lines: true" is a noop with strings generators, binary, objectMode, sync', testStreamLinesGenerator, simpleChunks, simpleChunks, true, true, false, execaSync); -test('"lines: true" is a noop big strings generators, objectMode, sync', testStreamLinesGenerator, [bigString], [bigString], true, false, false, execaSync); -test('"lines: true" is a noop big strings generators without newlines, objectMode, sync', testStreamLinesGenerator, [bigStringNoNewlines], [bigStringNoNewlines], true, false, false, execaSync); - -test('"lines: true" stops on stream error', async t => { - const cause = new Error(foobarString); - const error = await t.throwsAsync(getSimpleChunkSubprocessAsync({ - * stdout(line) { - if (line === noNewlinesChunks[2]) { - throw cause; - } - - yield line; - }, - })); - t.is(error.cause, cause); - t.deepEqual(error.stdout, noNewlinesChunks.slice(0, 2)); -}); - -test('"lines: true" stops on stream error event', async t => { - const cause = new Error(foobarString); - const subprocess = getSimpleChunkSubprocessAsync(); - subprocess.stdout.emit('error', cause); - const error = await t.throwsAsync(subprocess); - t.is(error.cause, cause); - t.deepEqual(error.stdout, []); -}); diff --git a/test/stdio/lines-max-buffer.js b/test/stdio/lines-max-buffer.js deleted file mode 100644 index fa7a2b5686..0000000000 --- a/test/stdio/lines-max-buffer.js +++ /dev/null @@ -1,50 +0,0 @@ -import test from 'ava'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {simpleLines, noNewlinesChunks, getSimpleChunkSubprocessAsync} from '../helpers/lines.js'; -import {assertErrorMessage} from '../helpers/max-buffer.js'; - -setFixtureDirectory(); - -const maxBuffer = simpleLines.length - 1; - -const testBelowMaxBuffer = async (t, lines) => { - const {isMaxBuffer, stdout} = await getSimpleChunkSubprocessAsync({lines, maxBuffer: maxBuffer + 1}); - t.false(isMaxBuffer); - t.deepEqual(stdout, noNewlinesChunks); -}; - -test('"lines: true" can be below "maxBuffer"', testBelowMaxBuffer, true); -test('"lines: true" can be below "maxBuffer", fd-specific', testBelowMaxBuffer, {stdout: true}); - -const testAboveMaxBuffer = async (t, lines) => { - const {isMaxBuffer, shortMessage, stdout} = await t.throwsAsync(getSimpleChunkSubprocessAsync({lines, maxBuffer})); - t.true(isMaxBuffer); - assertErrorMessage(t, shortMessage, {length: maxBuffer, unit: 'lines'}); - t.deepEqual(stdout, noNewlinesChunks.slice(0, maxBuffer)); -}; - -test('"lines: true" can be above "maxBuffer"', testAboveMaxBuffer, true); -test('"lines: true" can be above "maxBuffer", fd-specific', testAboveMaxBuffer, {stdout: true}); - -const testMaxBufferUnit = async (t, lines) => { - const {isMaxBuffer, shortMessage, stdout} = await t.throwsAsync(execa('noop-repeat.js', ['1', '...\n'], {lines, maxBuffer})); - t.true(isMaxBuffer); - assertErrorMessage(t, shortMessage, {length: maxBuffer, unit: 'lines'}); - t.deepEqual(stdout, ['...', '...']); -}; - -test('"maxBuffer" is measured in lines with "lines: true"', testMaxBufferUnit, true); -test('"maxBuffer" is measured in lines with "lines: true", fd-specific', testMaxBufferUnit, {stdout: true}); - -const testMaxBufferUnitSync = (t, lines) => { - const {isMaxBuffer, shortMessage, stdout} = t.throws(() => { - execaSync('noop-repeat.js', ['1', '...\n'], {lines, maxBuffer}); - }, {code: 'ENOBUFS'}); - t.true(isMaxBuffer); - assertErrorMessage(t, shortMessage, {execaMethod: execaSync, length: maxBuffer}); - t.deepEqual(stdout, ['..']); -}; - -test('"maxBuffer" is measured in bytes with "lines: true", sync', testMaxBufferUnitSync, true); -test('"maxBuffer" is measured in bytes with "lines: true", fd-specific, sync', testMaxBufferUnitSync, {stdout: true}); diff --git a/test/stdio/lines-mixed.js b/test/stdio/lines-mixed.js deleted file mode 100644 index fc2173d9f4..0000000000 --- a/test/stdio/lines-mixed.js +++ /dev/null @@ -1,60 +0,0 @@ -import {Writable} from 'node:stream'; -import test from 'ava'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {assertStreamOutput, assertStreamDataEvents, assertIterableChunks} from '../helpers/convert.js'; -import { - simpleFull, - simpleLines, - noNewlinesChunks, - getSimpleChunkSubprocessAsync, -} from '../helpers/lines.js'; - -setFixtureDirectory(); - -const testAsyncIteration = async (t, expectedLines, stripFinalNewline) => { - const subprocess = getSimpleChunkSubprocessAsync({stripFinalNewline}); - t.false(subprocess.stdout.readableObjectMode); - await assertStreamOutput(t, subprocess.stdout, simpleFull); - const {stdout} = await subprocess; - t.deepEqual(stdout, expectedLines); -}; - -test('"lines: true" works with stream async iteration', testAsyncIteration, simpleLines, false); -test('"lines: true" works with stream async iteration, stripFinalNewline', testAsyncIteration, noNewlinesChunks, true); - -const testDataEvents = async (t, expectedLines, stripFinalNewline) => { - const subprocess = getSimpleChunkSubprocessAsync({stripFinalNewline}); - await assertStreamDataEvents(t, subprocess.stdout, simpleFull); - const {stdout} = await subprocess; - t.deepEqual(stdout, expectedLines); -}; - -test('"lines: true" works with stream "data" events', testDataEvents, simpleLines, false); -test('"lines: true" works with stream "data" events, stripFinalNewline', testDataEvents, noNewlinesChunks, true); - -const testWritableStream = async (t, expectedLines, stripFinalNewline) => { - let output = ''; - const writable = new Writable({ - write(line, encoding, done) { - output += line.toString(); - done(); - }, - decodeStrings: false, - }); - const {stdout} = await getSimpleChunkSubprocessAsync({stripFinalNewline, stdout: ['pipe', writable]}); - t.deepEqual(output, simpleFull); - t.deepEqual(stdout, expectedLines); -}; - -test('"lines: true" works with writable streams targets', testWritableStream, simpleLines, false); -test('"lines: true" works with writable streams targets, stripFinalNewline', testWritableStream, noNewlinesChunks, true); - -const testIterable = async (t, expectedLines, stripFinalNewline) => { - const subprocess = getSimpleChunkSubprocessAsync({stripFinalNewline}); - await assertIterableChunks(t, subprocess, noNewlinesChunks); - const {stdout} = await subprocess; - t.deepEqual(stdout, expectedLines); -}; - -test('"lines: true" works with subprocess.iterable()', testIterable, simpleLines, false); -test('"lines: true" works with subprocess.iterable(), stripFinalNewline', testIterable, noNewlinesChunks, true); diff --git a/test/stdio/lines-noop.js b/test/stdio/lines-noop.js deleted file mode 100644 index 7543e14bed..0000000000 --- a/test/stdio/lines-noop.js +++ /dev/null @@ -1,89 +0,0 @@ -import test from 'ava'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {getOutputsGenerator} from '../helpers/generator.js'; -import {foobarObject} from '../helpers/input.js'; -import { - simpleFull, - simpleFullUint8Array, - simpleFullHex, - simpleFullUtf16Uint8Array, - simpleLines, - noNewlinesChunks, - getSimpleChunkSubprocess, -} from '../helpers/lines.js'; - -setFixtureDirectory(); - -const testStreamLinesNoop = async (t, lines, execaMethod) => { - const {stdout} = await execaMethod('noop-fd.js', ['1', simpleFull], {lines}); - t.is(stdout, simpleFull); -}; - -test('"lines: false" is a noop', testStreamLinesNoop, false, execa); -test('"lines: false" is a noop, fd-specific', testStreamLinesNoop, {stderr: true}, execa); -test('"lines: false" is a noop, fd-specific none', testStreamLinesNoop, {}, execa); -test('"lines: false" is a noop, sync', testStreamLinesNoop, false, execaSync); -test('"lines: false" is a noop, fd-specific, sync', testStreamLinesNoop, {stderr: true}, execaSync); -test('"lines: false" is a noop, fd-specific none, sync', testStreamLinesNoop, {}, execaSync); - -const testLinesObjectMode = async (t, lines, execaMethod) => { - const {stdout} = await execaMethod('noop.js', { - stdout: getOutputsGenerator([foobarObject])(true), - lines, - }); - t.deepEqual(stdout, [foobarObject]); -}; - -test('"lines: true" is a noop with objects generators, objectMode', testLinesObjectMode, true, execa); -test('"lines: true" is a noop with objects generators, fd-specific, objectMode', testLinesObjectMode, {stdout: true}, execa); -test('"lines: true" is a noop with objects generators, objectMode, sync', testLinesObjectMode, true, execaSync); -test('"lines: true" is a noop with objects generators, fd-specific, objectMode, sync', testLinesObjectMode, {stdout: true}, execaSync); - -// eslint-disable-next-line max-params -const testEncoding = async (t, input, expectedOutput, encoding, lines, stripFinalNewline, execaMethod) => { - const {stdout} = await execaMethod('stdin.js', { - lines, - stripFinalNewline, - encoding, - input, - }); - t.deepEqual(stdout, expectedOutput); -}; - -test('"lines: true" is a noop with "encoding: utf16"', testEncoding, simpleFullUtf16Uint8Array, simpleLines, 'utf16le', true, false, execa); -test('"lines: true" is a noop with "encoding: utf16", fd-specific', testEncoding, simpleFullUtf16Uint8Array, simpleLines, 'utf16le', {stdout: true}, false, execa); -test('"lines: true" is a noop with "encoding: utf16", stripFinalNewline', testEncoding, simpleFullUtf16Uint8Array, noNewlinesChunks, 'utf16le', true, true, execa); -test('"lines: true" is a noop with "encoding: utf16", stripFinalNewline, fd-specific', testEncoding, simpleFullUtf16Uint8Array, noNewlinesChunks, 'utf16le', true, {stdout: true}, execa); -test('"lines: true" is a noop with "encoding: buffer"', testEncoding, simpleFull, simpleFullUint8Array, 'buffer', true, false, execa); -test('"lines: true" is a noop with "encoding: buffer", fd-specific', testEncoding, simpleFull, simpleFullUint8Array, 'buffer', {stdout: true}, false, execa); -test('"lines: true" is a noop with "encoding: buffer", stripFinalNewline', testEncoding, simpleFull, simpleFullUint8Array, 'buffer', true, false, execa); -test('"lines: true" is a noop with "encoding: buffer", stripFinalNewline, fd-specific', testEncoding, simpleFull, simpleFullUint8Array, 'buffer', true, {stdout: false}, execa); -test('"lines: true" is a noop with "encoding: hex"', testEncoding, simpleFull, simpleFullHex, 'hex', true, false, execa); -test('"lines: true" is a noop with "encoding: hex", fd-specific', testEncoding, simpleFull, simpleFullHex, 'hex', {stdout: true}, false, execa); -test('"lines: true" is a noop with "encoding: hex", stripFinalNewline', testEncoding, simpleFull, simpleFullHex, 'hex', true, true, execa); -test('"lines: true" is a noop with "encoding: hex", stripFinalNewline, fd-specific', testEncoding, simpleFull, simpleFullHex, 'hex', true, {stdout: true}, execa); -test('"lines: true" is a noop with "encoding: utf16", sync', testEncoding, simpleFullUtf16Uint8Array, simpleLines, 'utf16le', true, false, execaSync); -test('"lines: true" is a noop with "encoding: utf16", fd-specific, sync', testEncoding, simpleFullUtf16Uint8Array, simpleLines, 'utf16le', {stdout: true}, false, execaSync); -test('"lines: true" is a noop with "encoding: utf16", stripFinalNewline, sync', testEncoding, simpleFullUtf16Uint8Array, noNewlinesChunks, 'utf16le', true, true, execaSync); -test('"lines: true" is a noop with "encoding: utf16", stripFinalNewline, fd-specific, sync', testEncoding, simpleFullUtf16Uint8Array, noNewlinesChunks, 'utf16le', true, {stdout: true}, execaSync); -test('"lines: true" is a noop with "encoding: buffer", sync', testEncoding, simpleFull, simpleFullUint8Array, 'buffer', true, false, execaSync); -test('"lines: true" is a noop with "encoding: buffer", fd-specific, sync', testEncoding, simpleFull, simpleFullUint8Array, 'buffer', {stdout: true}, false, execaSync); -test('"lines: true" is a noop with "encoding: buffer", stripFinalNewline, sync', testEncoding, simpleFull, simpleFullUint8Array, 'buffer', true, false, execaSync); -test('"lines: true" is a noop with "encoding: buffer", stripFinalNewline, fd-specific, sync', testEncoding, simpleFull, simpleFullUint8Array, 'buffer', true, {stdout: false}, execaSync); -test('"lines: true" is a noop with "encoding: hex", sync', testEncoding, simpleFull, simpleFullHex, 'hex', true, false, execaSync); -test('"lines: true" is a noop with "encoding: hex", fd-specific, sync', testEncoding, simpleFull, simpleFullHex, 'hex', {stdout: true}, false, execaSync); -test('"lines: true" is a noop with "encoding: hex", stripFinalNewline, sync', testEncoding, simpleFull, simpleFullHex, 'hex', true, true, execaSync); -test('"lines: true" is a noop with "encoding: hex", stripFinalNewline, fd-specific, sync', testEncoding, simpleFull, simpleFullHex, 'hex', true, {stdout: true}, execaSync); - -const testLinesNoBuffer = async (t, lines, buffer, execaMethod) => { - const {stdout} = await getSimpleChunkSubprocess(execaMethod, {lines, buffer}); - t.is(stdout, undefined); -}; - -test('"lines: true" is a noop with "buffer: false"', testLinesNoBuffer, true, false, execa); -test('"lines: true" is a noop with "buffer: false", fd-specific buffer', testLinesNoBuffer, true, {stdout: false}, execa); -test('"lines: true" is a noop with "buffer: false", fd-specific lines', testLinesNoBuffer, {stdout: true}, false, execa); -test('"lines: true" is a noop with "buffer: false", sync', testLinesNoBuffer, true, false, execaSync); -test('"lines: true" is a noop with "buffer: false", fd-specific buffer, sync', testLinesNoBuffer, true, {stdout: false}, execaSync); -test('"lines: true" is a noop with "buffer: false", fd-specific lines, sync', testLinesNoBuffer, {stdout: true}, false, execaSync); diff --git a/test/stdio/native-fd.js b/test/stdio/native-fd.js deleted file mode 100644 index 110f6361df..0000000000 --- a/test/stdio/native-fd.js +++ /dev/null @@ -1,76 +0,0 @@ -import {platform} from 'node:process'; -import test from 'ava'; -import {execa, execaSync} from '../../index.js'; -import {getStdio, fullStdio} from '../helpers/stdio.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString} from '../helpers/input.js'; -import {nestedSubprocess} from '../helpers/nested.js'; - -setFixtureDirectory(); - -const isLinux = platform === 'linux'; -const isWindows = platform === 'win32'; - -const testFd3InheritOutput = async (t, stdioOption, isSync) => { - const {stdio} = await nestedSubprocess('noop-fd.js', ['3', foobarString], {...getStdio(3, stdioOption), isSync}, fullStdio); - t.is(stdio[3], foobarString); -}; - -test('stdio[*] output can use "inherit"', testFd3InheritOutput, 'inherit', false); -test('stdio[*] output can use ["inherit"]', testFd3InheritOutput, ['inherit'], false); -test('stdio[*] output can use "inherit", sync', testFd3InheritOutput, 'inherit', true); -test('stdio[*] output can use ["inherit"], sync', testFd3InheritOutput, ['inherit'], true); - -if (isLinux) { - const testOverflowStream = async (t, fdNumber, stdioOption, isSync) => { - const {stdout} = await nestedSubprocess('empty.js', {...getStdio(fdNumber, stdioOption), isSync}, fullStdio); - t.is(stdout, ''); - }; - - test('stdin can use 4+', testOverflowStream, 0, 4, false); - test('stdin can use [4+]', testOverflowStream, 0, [4], false); - test('stdout can use 4+', testOverflowStream, 1, 4, false); - test('stdout can use [4+]', testOverflowStream, 1, [4], false); - test('stderr can use 4+', testOverflowStream, 2, 4, false); - test('stderr can use [4+]', testOverflowStream, 2, [4], false); - test('stdio[*] can use 4+', testOverflowStream, 3, 4, false); - test('stdio[*] can use [4+]', testOverflowStream, 3, [4], false); - test('stdin can use 4+, sync', testOverflowStream, 0, 4, true); - test('stdin can use [4+], sync', testOverflowStream, 0, [4], true); - test('stdout can use 4+, sync', testOverflowStream, 1, 4, true); - test('stdout can use [4+], sync', testOverflowStream, 1, [4], true); - test('stderr can use 4+, sync', testOverflowStream, 2, 4, true); - test('stderr can use [4+], sync', testOverflowStream, 2, [4], true); - test('stdio[*] can use 4+, sync', testOverflowStream, 3, 4, true); - test('stdio[*] can use [4+], sync', testOverflowStream, 3, [4], true); -} - -const testOverflowStreamArray = (t, fdNumber, stdioOption) => { - t.throws(() => { - execa('empty.js', getStdio(fdNumber, stdioOption)); - }, {message: /no such standard stream/}); -}; - -test('stdin cannot use 4+ and another value', testOverflowStreamArray, 0, [4, 'pipe']); -test('stdout cannot use 4+ and another value', testOverflowStreamArray, 1, [4, 'pipe']); -test('stderr cannot use 4+ and another value', testOverflowStreamArray, 2, [4, 'pipe']); -test('stdio[*] cannot use 4+ and another value', testOverflowStreamArray, 3, [4, 'pipe']); -test('stdio[*] cannot use "inherit" and another value', testOverflowStreamArray, 3, ['inherit', 'pipe']); - -const getInvalidFdCode = () => { - if (isLinux) { - return 'EINVAL'; - } - - return isWindows ? 'EBADF' : 'ENXIO'; -}; - -const testOverflowStreamArraySync = (t, fdNumber) => { - t.throws(() => { - execaSync('noop-fd.js', [fdNumber, foobarString], getStdio(fdNumber, [4, 'pipe'])); - }, {code: getInvalidFdCode()}); -}; - -test('stdout cannot use 4+ and another value, sync', testOverflowStreamArraySync, 1); -test('stderr cannot use 4+ and another value, sync', testOverflowStreamArraySync, 2); -test('stdio[*] cannot use 4+ and another value, sync', testOverflowStreamArraySync, 3); diff --git a/test/stdio/native-inherit-pipe.js b/test/stdio/native-inherit-pipe.js deleted file mode 100644 index 67423a4b38..0000000000 --- a/test/stdio/native-inherit-pipe.js +++ /dev/null @@ -1,97 +0,0 @@ -import {readFile, rm} from 'node:fs/promises'; -import test from 'ava'; -import tempfile from 'tempfile'; -import {execa} from '../../index.js'; -import {getStdio, fullStdio} from '../helpers/stdio.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString} from '../helpers/input.js'; -import {nestedSubprocess} from '../helpers/nested.js'; - -setFixtureDirectory(); - -const testInheritStdin = async (t, stdioOption, isSync) => { - const {stdout} = await execa('nested-multiple-stdin.js', [JSON.stringify(stdioOption), `${isSync}`], {input: foobarString}); - t.is(stdout, `${foobarString}${foobarString}`); -}; - -test('stdin can be ["inherit", "pipe"]', testInheritStdin, ['inherit', 'pipe'], false); -test('stdin can be [0, "pipe"]', testInheritStdin, [0, 'pipe'], false); -test('stdin can be [process.stdin, "pipe"]', testInheritStdin, ['stdin', 'pipe'], false); -test.serial('stdin can be ["inherit", "pipe"], sync', testInheritStdin, ['inherit', 'pipe'], true); -test.serial('stdin can be [0, "pipe"], sync', testInheritStdin, [0, 'pipe'], true); -test.serial('stdin can be [process.stdin, "pipe"], sync', testInheritStdin, ['stdin', 'pipe'], true); - -// eslint-disable-next-line max-params -const testInheritStdioOutput = async (t, fdNumber, outerFdNumber, stdioOption, isSync, encoding) => { - const {stdio} = await execa('nested-multiple-stdio-output.js', [JSON.stringify(stdioOption), `${fdNumber}`, `${outerFdNumber}`, `${isSync}`, encoding], fullStdio); - t.is(stdio[fdNumber], foobarString); - t.is(stdio[outerFdNumber], `nested ${foobarString}`); -}; - -test('stdout can be ["inherit", "pipe"]', testInheritStdioOutput, 1, 2, ['inherit', 'pipe'], false, 'utf8'); -test('stdout can be [1, "pipe"]', testInheritStdioOutput, 1, 2, [1, 'pipe'], false, 'utf8'); -test('stdout can be [process.stdout, "pipe"]', testInheritStdioOutput, 1, 2, ['stdout', 'pipe'], false, 'utf8'); -test('stderr can be ["inherit", "pipe"]', testInheritStdioOutput, 2, 1, ['inherit', 'pipe'], false, 'utf8'); -test('stderr can be [2, "pipe"]', testInheritStdioOutput, 2, 1, [2, 'pipe'], false, 'utf8'); -test('stderr can be [process.stderr, "pipe"]', testInheritStdioOutput, 2, 1, ['stderr', 'pipe'], false, 'utf8'); -test('stdout can be ["inherit", "pipe"], encoding "buffer"', testInheritStdioOutput, 1, 2, ['inherit', 'pipe'], false, 'buffer'); -test('stdout can be [1, "pipe"], encoding "buffer"', testInheritStdioOutput, 1, 2, [1, 'pipe'], false, 'buffer'); -test('stdout can be [process.stdout, "pipe"], encoding "buffer"', testInheritStdioOutput, 1, 2, ['stdout', 'pipe'], false, 'buffer'); -test('stderr can be ["inherit", "pipe"], encoding "buffer"', testInheritStdioOutput, 2, 1, ['inherit', 'pipe'], false, 'buffer'); -test('stderr can be [2, "pipe"], encoding "buffer"', testInheritStdioOutput, 2, 1, [2, 'pipe'], false, 'buffer'); -test('stderr can be [process.stderr, "pipe"], encoding "buffer"', testInheritStdioOutput, 2, 1, ['stderr', 'pipe'], false, 'buffer'); -test('stdout can be ["inherit", "pipe"], sync', testInheritStdioOutput, 1, 2, ['inherit', 'pipe'], true, 'utf8'); -test('stdout can be [1, "pipe"], sync', testInheritStdioOutput, 1, 2, [1, 'pipe'], true, 'utf8'); -test('stdout can be [process.stdout, "pipe"], sync', testInheritStdioOutput, 1, 2, ['stdout', 'pipe'], true, 'utf8'); -test('stderr can be ["inherit", "pipe"], sync', testInheritStdioOutput, 2, 1, ['inherit', 'pipe'], true, 'utf8'); -test('stderr can be [2, "pipe"], sync', testInheritStdioOutput, 2, 1, [2, 'pipe'], true, 'utf8'); -test('stderr can be [process.stderr, "pipe"], sync', testInheritStdioOutput, 2, 1, ['stderr', 'pipe'], true, 'utf8'); -test('stdio[*] output can be ["inherit", "pipe"], sync', testInheritStdioOutput, 3, 1, ['inherit', 'pipe'], true, 'utf8'); -test('stdio[*] output can be [3, "pipe"], sync', testInheritStdioOutput, 3, 1, [3, 'pipe'], true, 'utf8'); -test('stdout can be ["inherit", "pipe"], encoding "buffer", sync', testInheritStdioOutput, 1, 2, ['inherit', 'pipe'], true, 'buffer'); -test('stdout can be [1, "pipe"], encoding "buffer", sync', testInheritStdioOutput, 1, 2, [1, 'pipe'], true, 'buffer'); -test('stdout can be [process.stdout, "pipe"], encoding "buffer", sync', testInheritStdioOutput, 1, 2, ['stdout', 'pipe'], true, 'buffer'); -test('stderr can be ["inherit", "pipe"], encoding "buffer", sync', testInheritStdioOutput, 2, 1, ['inherit', 'pipe'], true, 'buffer'); -test('stderr can be [2, "pipe"], encoding "buffer", sync', testInheritStdioOutput, 2, 1, [2, 'pipe'], true, 'buffer'); -test('stderr can be [process.stderr, "pipe"], encoding "buffer", sync', testInheritStdioOutput, 2, 1, ['stderr', 'pipe'], true, 'buffer'); -test('stdio[*] output can be ["inherit", "pipe"], encoding "buffer", sync', testInheritStdioOutput, 3, 1, ['inherit', 'pipe'], true, 'buffer'); -test('stdio[*] output can be [3, "pipe"], encoding "buffer", sync', testInheritStdioOutput, 3, 1, [3, 'pipe'], true, 'buffer'); - -const testInheritNoBuffer = async (t, stdioOption, isSync) => { - const filePath = tempfile(); - await nestedSubprocess('nested-write.js', [filePath, foobarString], {stdin: stdioOption, buffer: false, isSync}, {input: foobarString}); - t.is(await readFile(filePath, 'utf8'), `${foobarString} ${foobarString}`); - await rm(filePath); -}; - -test('stdin can be ["inherit", "pipe"], buffer: false', testInheritNoBuffer, ['inherit', 'pipe'], false); -test('stdin can be [0, "pipe"], buffer: false', testInheritNoBuffer, [0, 'pipe'], false); -test.serial('stdin can be ["inherit", "pipe"], buffer: false, sync', testInheritNoBuffer, ['inherit', 'pipe'], true); -test.serial('stdin can be [0, "pipe"], buffer: false, sync', testInheritNoBuffer, [0, 'pipe'], true); - -test('stdin can use ["inherit", "pipe"] in a TTY', async t => { - const stdioOption = [['inherit', 'pipe'], 'inherit', 'pipe']; - const {stdout} = await execa('nested-sync-tty.js', [JSON.stringify({stdio: stdioOption}), 'false', 'stdin-fd.js', '0'], {input: foobarString}); - t.is(stdout, foobarString); -}); - -const testNoTtyInput = async (t, fdNumber, optionName) => { - const stdioOption = ['pipe', 'inherit', 'pipe']; - stdioOption[fdNumber] = [[''], 'inherit', 'pipe']; - const {message} = await t.throwsAsync(execa('nested-sync-tty.js', [JSON.stringify({stdio: stdioOption}), 'true', 'stdin-fd.js', `${fdNumber}`], fullStdio)); - t.true(message.includes(`The \`${optionName}: 'inherit'\` option is invalid: it cannot be a TTY`)); -}; - -test('stdin cannot use ["inherit", "pipe"] in a TTY, sync', testNoTtyInput, 0, 'stdin'); -test('stdio[*] input cannot use ["inherit", "pipe"] in a TTY, sync', testNoTtyInput, 3, 'stdio[3]'); - -const testTtyOutput = async (t, fdNumber, isSync) => { - const {stdio} = await execa('nested-sync-tty.js', [JSON.stringify(getStdio(fdNumber, ['inherit', 'pipe'])), `${isSync}`, 'noop-fd.js', `${fdNumber}`, foobarString], fullStdio); - t.is(stdio[fdNumber], foobarString); -}; - -test('stdout can use ["inherit", "pipe"] in a TTY', testTtyOutput, 1, false); -test('stderr can use ["inherit", "pipe"] in a TTY', testTtyOutput, 2, false); -test('stdout can use ["inherit", "pipe"] in a TTY, sync', testTtyOutput, 1, true); -test('stderr can use ["inherit", "pipe"] in a TTY, sync', testTtyOutput, 2, true); -test('stdio[*] output can use ["inherit", "pipe"] in a TTY, sync', testTtyOutput, 3, true); diff --git a/test/stdio/native-redirect.js b/test/stdio/native-redirect.js deleted file mode 100644 index d1da370f2d..0000000000 --- a/test/stdio/native-redirect.js +++ /dev/null @@ -1,75 +0,0 @@ -import test from 'ava'; -import {execa} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString} from '../helpers/input.js'; - -setFixtureDirectory(); - -// eslint-disable-next-line max-params -const testRedirect = async (t, stdioOption, fdNumber, isInput, isSync) => { - const {fixtureName, ...options} = isInput - ? {fixtureName: 'stdin-fd.js', input: foobarString} - : {fixtureName: 'noop-fd.js'}; - const {stdio} = await execa('nested-stdio.js', [JSON.stringify(stdioOption), `${fdNumber}`, `${isSync}`, fixtureName, foobarString], options); - const resultFdNumber = isStderrDescriptor(stdioOption) ? 2 : 1; - t.is(stdio[resultFdNumber], foobarString); -}; - -const isStderrDescriptor = stdioOption => stdioOption === 2 - || stdioOption === 'stderr' - || (Array.isArray(stdioOption) && isStderrDescriptor(stdioOption[0])); - -test.serial('stdio[*] can be 0', testRedirect, 0, 3, true, false); -test.serial('stdio[*] can be [0]', testRedirect, [0], 3, true, false); -test.serial('stdio[*] can be [0, "pipe"]', testRedirect, [0, 'pipe'], 3, true, false); -test.serial('stdio[*] can be process.stdin', testRedirect, 'stdin', 3, true, false); -test.serial('stdio[*] can be [process.stdin]', testRedirect, ['stdin'], 3, true, false); -test.serial('stdio[*] can be [process.stdin, "pipe"]', testRedirect, ['stdin', 'pipe'], 3, true, false); -test('stdout can be 2', testRedirect, 2, 1, false, false); -test('stdout can be [2]', testRedirect, [2], 1, false, false); -test('stdout can be [2, "pipe"]', testRedirect, [2, 'pipe'], 1, false, false); -test('stdout can be process.stderr', testRedirect, 'stderr', 1, false, false); -test('stdout can be [process.stderr]', testRedirect, ['stderr'], 1, false, false); -test('stdout can be [process.stderr, "pipe"]', testRedirect, ['stderr', 'pipe'], 1, false, false); -test('stderr can be 1', testRedirect, 1, 2, false, false); -test('stderr can be [1]', testRedirect, [1], 2, false, false); -test('stderr can be [1, "pipe"]', testRedirect, [1, 'pipe'], 2, false, false); -test('stderr can be process.stdout', testRedirect, 'stdout', 2, false, false); -test('stderr can be [process.stdout]', testRedirect, ['stdout'], 2, false, false); -test('stderr can be [process.stdout, "pipe"]', testRedirect, ['stdout', 'pipe'], 2, false, false); -test('stdio[*] can be 1', testRedirect, 1, 3, false, false); -test('stdio[*] can be [1]', testRedirect, [1], 3, false, false); -test('stdio[*] can be [1, "pipe"]', testRedirect, [1, 'pipe'], 3, false, false); -test('stdio[*] can be 2', testRedirect, 2, 3, false, false); -test('stdio[*] can be [2]', testRedirect, [2], 3, false, false); -test('stdio[*] can be [2, "pipe"]', testRedirect, [2, 'pipe'], 3, false, false); -test('stdio[*] can be process.stdout', testRedirect, 'stdout', 3, false, false); -test('stdio[*] can be [process.stdout]', testRedirect, ['stdout'], 3, false, false); -test('stdio[*] can be [process.stdout, "pipe"]', testRedirect, ['stdout', 'pipe'], 3, false, false); -test('stdio[*] can be process.stderr', testRedirect, 'stderr', 3, false, false); -test('stdio[*] can be [process.stderr]', testRedirect, ['stderr'], 3, false, false); -test('stdio[*] can be [process.stderr, "pipe"]', testRedirect, ['stderr', 'pipe'], 3, false, false); -test('stdout can be 2, sync', testRedirect, 2, 1, false, true); -test('stdout can be [2], sync', testRedirect, [2], 1, false, true); -test('stdout can be [2, "pipe"], sync', testRedirect, [2, 'pipe'], 1, false, true); -test('stdout can be process.stderr, sync', testRedirect, 'stderr', 1, false, true); -test('stdout can be [process.stderr], sync', testRedirect, ['stderr'], 1, false, true); -test('stdout can be [process.stderr, "pipe"], sync', testRedirect, ['stderr', 'pipe'], 1, false, true); -test('stderr can be 1, sync', testRedirect, 1, 2, false, true); -test('stderr can be [1], sync', testRedirect, [1], 2, false, true); -test('stderr can be [1, "pipe"], sync', testRedirect, [1, 'pipe'], 2, false, true); -test('stderr can be process.stdout, sync', testRedirect, 'stdout', 2, false, true); -test('stderr can be [process.stdout], sync', testRedirect, ['stdout'], 2, false, true); -test('stderr can be [process.stdout, "pipe"], sync', testRedirect, ['stdout', 'pipe'], 2, false, true); -test('stdio[*] can be 1, sync', testRedirect, 1, 3, false, true); -test('stdio[*] can be [1], sync', testRedirect, [1], 3, false, true); -test('stdio[*] can be [1, "pipe"], sync', testRedirect, [1, 'pipe'], 3, false, true); -test('stdio[*] can be 2, sync', testRedirect, 2, 3, false, true); -test('stdio[*] can be [2], sync', testRedirect, [2], 3, false, true); -test('stdio[*] can be [2, "pipe"], sync', testRedirect, [2, 'pipe'], 3, false, true); -test('stdio[*] can be process.stdout, sync', testRedirect, 'stdout', 3, false, true); -test('stdio[*] can be [process.stdout], sync', testRedirect, ['stdout'], 3, false, true); -test('stdio[*] can be [process.stdout, "pipe"], sync', testRedirect, ['stdout', 'pipe'], 3, false, true); -test('stdio[*] can be process.stderr, sync', testRedirect, 'stderr', 3, false, true); -test('stdio[*] can be [process.stderr], sync', testRedirect, ['stderr'], 3, false, true); -test('stdio[*] can be [process.stderr, "pipe"], sync', testRedirect, ['stderr', 'pipe'], 3, false, true); diff --git a/test/stdio/node-stream-custom.js b/test/stdio/node-stream-custom.js deleted file mode 100644 index 901b43e859..0000000000 --- a/test/stdio/node-stream-custom.js +++ /dev/null @@ -1,160 +0,0 @@ -import {createReadStream, createWriteStream} from 'node:fs'; -import {readFile, writeFile, rm} from 'node:fs/promises'; -import {Writable, PassThrough} from 'node:stream'; -import {text} from 'node:stream/consumers'; -import {setImmediate} from 'node:timers/promises'; -import {callbackify} from 'node:util'; -import test from 'ava'; -import tempfile from 'tempfile'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {getStdio} from '../helpers/stdio.js'; -import {foobarString} from '../helpers/input.js'; -import {noopReadable, noopWritable} from '../helpers/stream.js'; -import {getEarlyErrorSubprocess, expectedEarlyError} from '../helpers/early-error.js'; - -setFixtureDirectory(); - -const testLazyFileReadable = async (t, fdNumber) => { - const filePath = tempfile(); - await writeFile(filePath, 'foobar'); - const stream = createReadStream(filePath); - - const {stdout} = await execa('stdin-fd.js', [`${fdNumber}`], getStdio(fdNumber, [stream, 'pipe'])); - t.is(stdout, 'foobar'); - - await rm(filePath); -}; - -test('stdin can be [Readable, "pipe"] without a file descriptor', testLazyFileReadable, 0); -test('stdio[*] can be [Readable, "pipe"] without a file descriptor', testLazyFileReadable, 3); - -const testLazyFileReadableSync = (t, fdNumber) => { - t.throws(() => { - execaSync('stdin-fd.js', [`${fdNumber}`], getStdio(fdNumber, [noopReadable(), 'pipe'])); - }, {message: /cannot both be an array and include a stream/}); -}; - -test('stdin cannot be [Readable, "pipe"] without a file descriptor, sync', testLazyFileReadableSync, 0); -test('stdio[*] cannot be [Readable, "pipe"] without a file descriptor, sync', testLazyFileReadableSync, 3); - -const testLazyFileWritable = async (t, fdNumber) => { - const filePath = tempfile(); - const stream = createWriteStream(filePath); - - await execa('noop-fd.js', [`${fdNumber}`, 'foobar'], getStdio(fdNumber, [stream, 'pipe'])); - t.is(await readFile(filePath, 'utf8'), 'foobar'); - - await rm(filePath); -}; - -test('stdout can be [Writable, "pipe"] without a file descriptor', testLazyFileWritable, 1); -test('stderr can be [Writable, "pipe"] without a file descriptor', testLazyFileWritable, 2); -test('stdio[*] can be [Writable, "pipe"] without a file descriptor', testLazyFileWritable, 3); - -const testLazyFileWritableSync = (t, fdNumber) => { - t.throws(() => { - execaSync('noop-fd.js', [`${fdNumber}`], getStdio(fdNumber, [noopWritable(), 'pipe'])); - }, {message: /cannot both be an array and include a stream/}); -}; - -test('stdout cannot be [Writable, "pipe"] without a file descriptor, sync', testLazyFileWritableSync, 1); -test('stderr cannot be [Writable, "pipe"] without a file descriptor, sync', testLazyFileWritableSync, 2); -test('stdio[*] cannot be [Writable, "pipe"] without a file descriptor, sync', testLazyFileWritableSync, 3); - -test('Waits for custom streams destroy on subprocess errors', async t => { - let waitedForDestroy = false; - const stream = new Writable({ - destroy: callbackify(async error => { - await setImmediate(); - waitedForDestroy = true; - return error; - }), - }); - const {timedOut} = await t.throwsAsync(execa('forever.js', {stdout: [stream, 'pipe'], timeout: 1})); - t.true(timedOut); - t.true(waitedForDestroy); -}); - -test('Handles custom streams destroy errors on subprocess success', async t => { - const cause = new Error('test'); - const stream = new Writable({ - destroy(destroyError, done) { - done(destroyError ?? cause); - }, - }); - const error = await t.throwsAsync(execa('empty.js', {stdout: [stream, 'pipe']})); - t.is(error.cause, cause); -}); - -const testStreamEarlyExit = async (t, stream, streamName) => { - const error = await t.throwsAsync(getEarlyErrorSubprocess({[streamName]: [stream, 'pipe']})); - t.like(error, expectedEarlyError); - t.true(stream.destroyed); -}; - -test('Input streams are canceled on early subprocess exit', testStreamEarlyExit, noopReadable(), 'stdin'); -test('Output streams are canceled on early subprocess exit', testStreamEarlyExit, noopWritable(), 'stdout'); - -const testInputDuplexStream = async (t, fdNumber) => { - const stream = new PassThrough(); - stream.end(foobarString); - const {stdout} = await execa('stdin-fd.js', [`${fdNumber}`], getStdio(fdNumber, [stream, new Uint8Array()])); - t.is(stdout, foobarString); -}; - -test('Can pass Duplex streams to stdin', testInputDuplexStream, 0); -test('Can pass Duplex streams to input stdio[*]', testInputDuplexStream, 3); - -const testOutputDuplexStream = async (t, fdNumber) => { - const stream = new PassThrough(); - const [output] = await Promise.all([ - text(stream), - execa('noop-fd.js', [`${fdNumber}`], getStdio(fdNumber, [stream, 'pipe'])), - ]); - t.is(output, foobarString); -}; - -test('Can pass Duplex streams to stdout', testOutputDuplexStream, 1); -test('Can pass Duplex streams to stderr', testOutputDuplexStream, 2); -test('Can pass Duplex streams to output stdio[*]', testOutputDuplexStream, 3); - -const testInputStreamAbort = async (t, fdNumber) => { - const stream = new PassThrough(); - stream.destroy(); - - const subprocess = execa('stdin-fd.js', [`${fdNumber}`], getStdio(fdNumber, [stream, new Uint8Array()])); - await subprocess; - t.true(subprocess.stdio[fdNumber].writableEnded); -}; - -test('subprocess.stdin is ended when an input stream aborts', testInputStreamAbort, 0); -test('subprocess.stdio[*] is ended when an input stream aborts', testInputStreamAbort, 3); - -const testInputStreamError = async (t, fdNumber) => { - const stream = new PassThrough(); - const cause = new Error(foobarString); - stream.destroy(cause); - - const subprocess = execa('stdin-fd.js', [`${fdNumber}`], getStdio(fdNumber, [stream, new Uint8Array()])); - t.like(await t.throwsAsync(subprocess), {cause}); - t.true(subprocess.stdio[fdNumber].writableEnded); -}; - -test('subprocess.stdin is ended when an input stream errors', testInputStreamError, 0); -test('subprocess.stdio[*] is ended when an input stream errors', testInputStreamError, 3); - -const testOutputStreamError = async (t, fdNumber) => { - const stream = new PassThrough(); - const cause = new Error(foobarString); - stream.destroy(cause); - - const subprocess = execa('noop-fd.js', [`${fdNumber}`], getStdio(fdNumber, [stream, 'pipe'])); - t.like(await t.throwsAsync(subprocess), {cause}); - t.true(subprocess.stdio[fdNumber].readableAborted); - t.is(subprocess.stdio[fdNumber].errored, null); -}; - -test('subprocess.stdout is aborted when an output stream errors', testOutputStreamError, 1); -test('subprocess.stderr is aborted when an output stream errors', testOutputStreamError, 2); -test('subprocess.stdio[*] is aborted when an output stream errors', testOutputStreamError, 3); diff --git a/test/stdio/node-stream-native.js b/test/stdio/node-stream-native.js deleted file mode 100644 index 4a651c366a..0000000000 --- a/test/stdio/node-stream-native.js +++ /dev/null @@ -1,185 +0,0 @@ -import {once} from 'node:events'; -import {createReadStream, createWriteStream} from 'node:fs'; -import {readFile, writeFile, rm} from 'node:fs/promises'; -import test from 'ava'; -import tempfile from 'tempfile'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {getStdio} from '../helpers/stdio.js'; -import {foobarString} from '../helpers/input.js'; -import { - noopReadable, - noopWritable, - noopDuplex, - simpleReadable, -} from '../helpers/stream.js'; - -setFixtureDirectory(); - -const testNoFileStreamSync = async (t, fdNumber, stream) => { - t.throws(() => { - execaSync('empty.js', getStdio(fdNumber, stream)); - }, {code: 'ERR_INVALID_ARG_VALUE'}); -}; - -test('stdin cannot be a Node.js Readable without a file descriptor - sync', testNoFileStreamSync, 0, noopReadable()); -test('stdin cannot be a Node.js Duplex without a file descriptor - sync', testNoFileStreamSync, 0, noopDuplex()); -test('stdout cannot be a Node.js Writable without a file descriptor - sync', testNoFileStreamSync, 1, noopWritable()); -test('stdout cannot be a Node.js Duplex without a file descriptor - sync', testNoFileStreamSync, 1, noopDuplex()); -test('stderr cannot be a Node.js Writable without a file descriptor - sync', testNoFileStreamSync, 2, noopWritable()); -test('stderr cannot be a Node.js Duplex without a file descriptor - sync', testNoFileStreamSync, 2, noopDuplex()); -test('stdio[*] cannot be a Node.js Readable without a file descriptor - sync', testNoFileStreamSync, 3, noopReadable()); -test('stdio[*] cannot be a Node.js Writable without a file descriptor - sync', testNoFileStreamSync, 3, noopWritable()); -test('stdio[*] cannot be a Node.js Duplex without a file descriptor - sync', testNoFileStreamSync, 3, noopDuplex()); - -test('input can be a Node.js Readable without a file descriptor', async t => { - const {stdout} = await execa('stdin.js', {input: simpleReadable()}); - t.is(stdout, foobarString); -}); - -test('input cannot be a Node.js Readable without a file descriptor - sync', t => { - t.throws(() => { - execaSync('empty.js', {input: simpleReadable()}); - }, {message: 'The `input` option cannot be a Node.js stream with synchronous methods.'}); -}); - -const testNoFileStream = async (t, fdNumber, stream) => { - await t.throwsAsync(execa('empty.js', getStdio(fdNumber, stream)), {code: 'ERR_INVALID_ARG_VALUE'}); -}; - -test('stdin cannot be a Node.js Readable without a file descriptor', testNoFileStream, 0, noopReadable()); -test('stdin cannot be a Node.js Duplex without a file descriptor', testNoFileStream, 0, noopDuplex()); -test('stdout cannot be a Node.js Writable without a file descriptor', testNoFileStream, 1, noopWritable()); -test('stdout cannot be a Node.js Duplex without a file descriptor', testNoFileStream, 1, noopDuplex()); -test('stderr cannot be a Node.js Writable without a file descriptor', testNoFileStream, 2, noopWritable()); -test('stderr cannot be a Node.js Duplex without a file descriptor', testNoFileStream, 2, noopDuplex()); -test('stdio[*] cannot be a Node.js Readable without a file descriptor', testNoFileStream, 3, noopReadable()); -test('stdio[*] cannot be a Node.js Writable without a file descriptor', testNoFileStream, 3, noopWritable()); -test('stdio[*] cannot be a Node.js Duplex without a file descriptor', testNoFileStream, 3, noopDuplex()); - -const createFileReadStream = async () => { - const filePath = tempfile(); - await writeFile(filePath, 'foobar'); - const stream = createReadStream(filePath); - await once(stream, 'open'); - return {stream, filePath}; -}; - -const createFileWriteStream = async () => { - const filePath = tempfile(); - const stream = createWriteStream(filePath); - await once(stream, 'open'); - return {stream, filePath}; -}; - -const assertFileStreamError = async (t, subprocess, stream, filePath) => { - const cause = new Error('test'); - stream.destroy(cause); - - const error = await t.throwsAsync(subprocess); - t.is(error.cause, cause); - t.is(error.exitCode, 0); - t.is(error.signal, undefined); - - await rm(filePath); -}; - -const testFileReadable = async (t, fdNumber, execaMethod) => { - const {stream, filePath} = await createFileReadStream(); - - const fdNumberString = fdNumber === 'input' ? '0' : `${fdNumber}`; - const {stdout} = await execaMethod('stdin-fd.js', [fdNumberString], getStdio(fdNumber, stream)); - t.is(stdout, 'foobar'); - - await rm(filePath); -}; - -test('input can be a Node.js Readable with a file descriptor', testFileReadable, 'input', execa); -test('stdin can be a Node.js Readable with a file descriptor', testFileReadable, 0, execa); -test('stdio[*] can be a Node.js Readable with a file descriptor', testFileReadable, 3, execa); -test('stdin can be a Node.js Readable with a file descriptor - sync', testFileReadable, 0, execaSync); -test('stdio[*] can be a Node.js Readable with a file descriptor - sync', testFileReadable, 3, execaSync); - -const testFileReadableError = async (t, fdNumber) => { - const {stream, filePath} = await createFileReadStream(); - - const fdNumberString = fdNumber === 'input' ? '0' : `${fdNumber}`; - const subprocess = execa('stdin-fd.js', [fdNumberString], getStdio(fdNumber, stream)); - - await assertFileStreamError(t, subprocess, stream, filePath); -}; - -test.serial('input handles errors from a Node.js Readable with a file descriptor', testFileReadableError, 'input'); -test.serial('stdin handles errors from a Node.js Readable with a file descriptor', testFileReadableError, 0); -test.serial('stdio[*] handles errors from a Node.js Readable with a file descriptor', testFileReadableError, 3); - -const testFileReadableOpen = async (t, fdNumber, useSingle, execaMethod) => { - const {stream, filePath} = await createFileReadStream(); - t.deepEqual(stream.eventNames(), []); - - const stdioOption = useSingle ? stream : [stream, 'pipe']; - await execaMethod('empty.js', getStdio(fdNumber, stdioOption)); - - t.is(stream.readable, useSingle && fdNumber !== 'input'); - t.deepEqual(stream.eventNames(), []); - - await rm(filePath); -}; - -test('input closes a Node.js Readable with a file descriptor', testFileReadableOpen, 'input', true, execa); -test('stdin leaves open a single Node.js Readable with a file descriptor', testFileReadableOpen, 0, true, execa); -test('stdin closes a combined Node.js Readable with a file descriptor', testFileReadableOpen, 0, false, execa); -test('stdio[*] leaves open a single Node.js Readable with a file descriptor', testFileReadableOpen, 3, true, execa); -test('stdin leaves open a single Node.js Readable with a file descriptor - sync', testFileReadableOpen, 0, true, execaSync); -test('stdio[*] leaves open a single Node.js Readable with a file descriptor - sync', testFileReadableOpen, 3, true, execaSync); - -const testFileWritable = async (t, fdNumber, execaMethod) => { - const {stream, filePath} = await createFileWriteStream(); - - await execaMethod('noop-fd.js', [`${fdNumber}`, 'foobar'], getStdio(fdNumber, stream)); - t.is(await readFile(filePath, 'utf8'), 'foobar'); - - await rm(filePath); -}; - -test('stdout can be a Node.js Writable with a file descriptor', testFileWritable, 1, execa); -test('stderr can be a Node.js Writable with a file descriptor', testFileWritable, 2, execa); -test('stdio[*] can be a Node.js Writable with a file descriptor', testFileWritable, 3, execa); -test('stdout can be a Node.js Writable with a file descriptor - sync', testFileWritable, 1, execaSync); -test('stderr can be a Node.js Writable with a file descriptor - sync', testFileWritable, 2, execaSync); -test('stdio[*] can be a Node.js Writable with a file descriptor - sync', testFileWritable, 3, execaSync); - -const testFileWritableError = async (t, fdNumber) => { - const {stream, filePath} = await createFileWriteStream(); - - const subprocess = execa('noop-fd.js', [`${fdNumber}`, foobarString], getStdio(fdNumber, stream)); - - await assertFileStreamError(t, subprocess, stream, filePath); -}; - -test.serial('stdout handles errors from a Node.js Writable with a file descriptor', testFileWritableError, 1); -test.serial('stderr handles errors from a Node.js Writable with a file descriptor', testFileWritableError, 2); -test.serial('stdio[*] handles errors from a Node.js Writable with a file descriptor', testFileWritableError, 3); - -const testFileWritableOpen = async (t, fdNumber, useSingle, execaMethod) => { - const {stream, filePath} = await createFileWriteStream(); - t.deepEqual(stream.eventNames(), []); - - const stdioOption = useSingle ? stream : [stream, 'pipe']; - await execaMethod('empty.js', getStdio(fdNumber, stdioOption)); - - t.is(stream.writable, useSingle); - t.deepEqual(stream.eventNames(), []); - - await rm(filePath); -}; - -test('stdout leaves open a single Node.js Writable with a file descriptor', testFileWritableOpen, 1, true, execa); -test('stdout closes a combined Node.js Writable with a file descriptor', testFileWritableOpen, 1, false, execa); -test('stderr leaves open a single Node.js Writable with a file descriptor', testFileWritableOpen, 2, true, execa); -test('stderr closes a combined Node.js Writable with a file descriptor', testFileWritableOpen, 2, false, execa); -test('stdio[*] leaves open a single Node.js Writable with a file descriptor', testFileWritableOpen, 3, true, execa); -test('stdio[*] closes a combined Node.js Writable with a file descriptor', testFileWritableOpen, 3, false, execa); -test('stdout leaves open a single Node.js Writable with a file descriptor - sync', testFileWritableOpen, 1, true, execaSync); -test('stderr leaves open a single Node.js Writable with a file descriptor - sync', testFileWritableOpen, 2, true, execaSync); -test('stdio[*] leaves open a single Node.js Writable with a file descriptor - sync', testFileWritableOpen, 3, true, execaSync); diff --git a/test/stdio/stdio-option.js b/test/stdio/stdio-option.js deleted file mode 100644 index 2054d8fe08..0000000000 --- a/test/stdio/stdio-option.js +++ /dev/null @@ -1,54 +0,0 @@ -import {inspect} from 'node:util'; -import test from 'ava'; -import {normalizeStdioOption} from '../../lib/stdio/stdio-option.js'; - -const stdioMacro = (t, input, expected) => { - if (expected instanceof Error) { - t.throws(() => { - normalizeStdioOption(input); - }, {message: expected.message}); - return; - } - - t.deepEqual(normalizeStdioOption(input), expected); -}; - -stdioMacro.title = (_, input) => `execa() ${(inspect(input))}`; - -test(stdioMacro, {stdio: 'inherit'}, ['inherit', 'inherit', 'inherit']); -test(stdioMacro, {stdio: 'pipe'}, ['pipe', 'pipe', 'pipe']); -test(stdioMacro, {stdio: 'ignore'}, ['ignore', 'ignore', 'ignore']); - -test(stdioMacro, {}, ['pipe', 'pipe', 'pipe']); -test(stdioMacro, {stdio: []}, ['pipe', 'pipe', 'pipe']); -test(stdioMacro, {stdio: [0]}, [0, 'pipe', 'pipe']); -test(stdioMacro, {stdio: [0, 1]}, [0, 1, 'pipe']); -test(stdioMacro, {stdio: [0, 1, 2]}, [0, 1, 2]); -test(stdioMacro, {stdio: [0, 1, 2, 3]}, [0, 1, 2, 3]); -test(stdioMacro, {stdio: [undefined, 1, 2]}, ['pipe', 1, 2]); -test(stdioMacro, {stdio: [null, 1, 2]}, ['pipe', 1, 2]); -test(stdioMacro, {stdio: [0, undefined, 2]}, [0, 'pipe', 2]); -test(stdioMacro, {stdio: [0, null, 2]}, [0, 'pipe', 2]); -test(stdioMacro, {stdio: [0, 1, undefined]}, [0, 1, 'pipe']); -test(stdioMacro, {stdio: [0, 1, null]}, [0, 1, 'pipe']); -test(stdioMacro, {stdio: [0, 1, 2, undefined]}, [0, 1, 2, 'ignore']); -test(stdioMacro, {stdio: [0, 1, 2, null]}, [0, 1, 2, 'ignore']); - -test(stdioMacro, {stdin: 'pipe'}, ['pipe', 'pipe', 'pipe']); -test(stdioMacro, {stdout: 'ignore'}, ['pipe', 'ignore', 'pipe']); -test(stdioMacro, {stderr: 'inherit'}, ['pipe', 'pipe', 'inherit']); -test(stdioMacro, {stdin: 'pipe', stdout: 'ignore', stderr: 'inherit'}, ['pipe', 'ignore', 'inherit']); -test(stdioMacro, {stdin: 'pipe', stdout: 'ignore'}, ['pipe', 'ignore', 'pipe']); -test(stdioMacro, {stdin: 'pipe', stderr: 'inherit'}, ['pipe', 'pipe', 'inherit']); -test(stdioMacro, {stdout: 'ignore', stderr: 'inherit'}, ['pipe', 'ignore', 'inherit']); -test(stdioMacro, {stdin: 0, stdout: 1, stderr: 2}, [0, 1, 2]); -test(stdioMacro, {stdin: 0, stdout: 1}, [0, 1, 'pipe']); -test(stdioMacro, {stdin: 0, stderr: 2}, [0, 'pipe', 2]); -test(stdioMacro, {stdout: 1, stderr: 2}, ['pipe', 1, 2]); - -test(stdioMacro, {stdio: {foo: 'bar'}}, new TypeError('Expected `stdio` to be of type `string` or `Array`, got `object`')); - -test(stdioMacro, {stdin: 'inherit', stdio: 'pipe'}, new Error('It\'s not possible to provide `stdio` in combination with one of `stdin`, `stdout`, `stderr`')); -test(stdioMacro, {stdin: 'inherit', stdio: ['pipe']}, new Error('It\'s not possible to provide `stdio` in combination with one of `stdin`, `stdout`, `stderr`')); -test(stdioMacro, {stdin: 'inherit', stdio: [undefined, 'pipe']}, new Error('It\'s not possible to provide `stdio` in combination with one of `stdin`, `stdout`, `stderr`')); -test(stdioMacro, {stdin: 0, stdio: 'pipe'}, new Error('It\'s not possible to provide `stdio` in combination with one of `stdin`, `stdout`, `stderr`')); diff --git a/test/stdio/type.js b/test/stdio/type.js deleted file mode 100644 index 7407dbf2af..0000000000 --- a/test/stdio/type.js +++ /dev/null @@ -1,130 +0,0 @@ -import test from 'ava'; -import {execa, execaSync} from '../../index.js'; -import {getStdio} from '../helpers/stdio.js'; -import {noopGenerator, uppercaseGenerator} from '../helpers/generator.js'; -import {uppercaseBufferDuplex} from '../helpers/duplex.js'; -import {uppercaseBufferWebTransform} from '../helpers/web-transform.js'; -import {generatorsMap} from '../helpers/map.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; - -setFixtureDirectory(); - -const testInvalidGenerator = (t, fdNumber, stdioOption, execaMethod) => { - t.throws(() => { - execaMethod('empty.js', getStdio(fdNumber, {...noopGenerator(), ...stdioOption})); - }, {message: 'final' in stdioOption ? /must be a generator/ : /must be a generator, a Duplex stream or a web TransformStream/}); -}; - -test('Cannot use invalid "transform" with stdin', testInvalidGenerator, 0, {transform: true}, execa); -test('Cannot use invalid "transform" with stdout', testInvalidGenerator, 1, {transform: true}, execa); -test('Cannot use invalid "transform" with stderr', testInvalidGenerator, 2, {transform: true}, execa); -test('Cannot use invalid "transform" with stdio[*]', testInvalidGenerator, 3, {transform: true}, execa); -test('Cannot use invalid "final" with stdin', testInvalidGenerator, 0, {final: true}, execa); -test('Cannot use invalid "final" with stdout', testInvalidGenerator, 1, {final: true}, execa); -test('Cannot use invalid "final" with stderr', testInvalidGenerator, 2, {final: true}, execa); -test('Cannot use invalid "final" with stdio[*]', testInvalidGenerator, 3, {final: true}, execa); -test('Cannot use invalid "transform" with stdin, sync', testInvalidGenerator, 0, {transform: true}, execaSync); -test('Cannot use invalid "transform" with stdout, sync', testInvalidGenerator, 1, {transform: true}, execaSync); -test('Cannot use invalid "transform" with stderr, sync', testInvalidGenerator, 2, {transform: true}, execaSync); -test('Cannot use invalid "transform" with stdio[*], sync', testInvalidGenerator, 3, {transform: true}, execaSync); -test('Cannot use invalid "final" with stdin, sync', testInvalidGenerator, 0, {final: true}, execaSync); -test('Cannot use invalid "final" with stdout, sync', testInvalidGenerator, 1, {final: true}, execaSync); -test('Cannot use invalid "final" with stderr, sync', testInvalidGenerator, 2, {final: true}, execaSync); -test('Cannot use invalid "final" with stdio[*], sync', testInvalidGenerator, 3, {final: true}, execaSync); - -// eslint-disable-next-line max-params -const testInvalidBinary = (t, fdNumber, optionName, type, execaMethod) => { - t.throws(() => { - execaMethod('empty.js', getStdio(fdNumber, {...generatorsMap[type].uppercase(), [optionName]: 'true'})); - }, {message: /a boolean/}); -}; - -test('Cannot use invalid "binary" with stdin', testInvalidBinary, 0, 'binary', 'generator', execa); -test('Cannot use invalid "binary" with stdout', testInvalidBinary, 1, 'binary', 'generator', execa); -test('Cannot use invalid "binary" with stderr', testInvalidBinary, 2, 'binary', 'generator', execa); -test('Cannot use invalid "binary" with stdio[*]', testInvalidBinary, 3, 'binary', 'generator', execa); -test('Cannot use invalid "objectMode" with stdin, generators', testInvalidBinary, 0, 'objectMode', 'generator', execa); -test('Cannot use invalid "objectMode" with stdout, generators', testInvalidBinary, 1, 'objectMode', 'generator', execa); -test('Cannot use invalid "objectMode" with stderr, generators', testInvalidBinary, 2, 'objectMode', 'generator', execa); -test('Cannot use invalid "objectMode" with stdio[*], generators', testInvalidBinary, 3, 'objectMode', 'generator', execa); -test('Cannot use invalid "binary" with stdin, sync', testInvalidBinary, 0, 'binary', 'generator', execaSync); -test('Cannot use invalid "binary" with stdout, sync', testInvalidBinary, 1, 'binary', 'generator', execaSync); -test('Cannot use invalid "binary" with stderr, sync', testInvalidBinary, 2, 'binary', 'generator', execaSync); -test('Cannot use invalid "binary" with stdio[*], sync', testInvalidBinary, 3, 'binary', 'generator', execaSync); -test('Cannot use invalid "objectMode" with stdin, generators, sync', testInvalidBinary, 0, 'objectMode', 'generator', execaSync); -test('Cannot use invalid "objectMode" with stdout, generators, sync', testInvalidBinary, 1, 'objectMode', 'generator', execaSync); -test('Cannot use invalid "objectMode" with stderr, generators, sync', testInvalidBinary, 2, 'objectMode', 'generator', execaSync); -test('Cannot use invalid "objectMode" with stdio[*], generators, sync', testInvalidBinary, 3, 'objectMode', 'generator', execaSync); -test('Cannot use invalid "objectMode" with stdin, duplexes', testInvalidBinary, 0, 'objectMode', 'duplex', execa); -test('Cannot use invalid "objectMode" with stdout, duplexes', testInvalidBinary, 1, 'objectMode', 'duplex', execa); -test('Cannot use invalid "objectMode" with stderr, duplexes', testInvalidBinary, 2, 'objectMode', 'duplex', execa); -test('Cannot use invalid "objectMode" with stdio[*], duplexes', testInvalidBinary, 3, 'objectMode', 'duplex', execa); -test('Cannot use invalid "objectMode" with stdin, webTransforms', testInvalidBinary, 0, 'objectMode', 'webTransform', execa); -test('Cannot use invalid "objectMode" with stdout, webTransforms', testInvalidBinary, 1, 'objectMode', 'webTransform', execa); -test('Cannot use invalid "objectMode" with stderr, webTransforms', testInvalidBinary, 2, 'objectMode', 'webTransform', execa); -test('Cannot use invalid "objectMode" with stdio[*], webTransforms', testInvalidBinary, 3, 'objectMode', 'webTransform', execa); - -// eslint-disable-next-line max-params -const testUndefinedOption = (t, fdNumber, optionName, type, optionValue) => { - t.throws(() => { - execa('empty.js', getStdio(fdNumber, {...generatorsMap[type].uppercase(), [optionName]: optionValue})); - }, {message: /can only be defined when using a generator/}); -}; - -test('Cannot use "binary" with duplexes and stdin', testUndefinedOption, 0, 'binary', 'duplex', true); -test('Cannot use "binary" with duplexes and stdout', testUndefinedOption, 1, 'binary', 'duplex', true); -test('Cannot use "binary" with duplexes and stderr', testUndefinedOption, 2, 'binary', 'duplex', true); -test('Cannot use "binary" with duplexes and stdio[*]', testUndefinedOption, 3, 'binary', 'duplex', true); -test('Cannot use "final" with duplexes and stdin', testUndefinedOption, 0, 'final', 'duplex', uppercaseBufferDuplex().transform); -test('Cannot use "final" with duplexes and stdout', testUndefinedOption, 1, 'final', 'duplex', uppercaseBufferDuplex().transform); -test('Cannot use "final" with duplexes and stderr', testUndefinedOption, 2, 'final', 'duplex', uppercaseBufferDuplex().transform); -test('Cannot use "final" with duplexes and stdio[*]', testUndefinedOption, 3, 'final', 'duplex', uppercaseBufferDuplex().transform); -test('Cannot use "binary" with webTransforms and stdin', testUndefinedOption, 0, 'binary', 'webTransform', true); -test('Cannot use "binary" with webTransforms and stdout', testUndefinedOption, 1, 'binary', 'webTransform', true); -test('Cannot use "binary" with webTransforms and stderr', testUndefinedOption, 2, 'binary', 'webTransform', true); -test('Cannot use "binary" with webTransforms and stdio[*]', testUndefinedOption, 3, 'binary', 'webTransform', true); -test('Cannot use "final" with webTransforms and stdin', testUndefinedOption, 0, 'final', 'webTransform', uppercaseBufferWebTransform().transform); -test('Cannot use "final" with webTransforms and stdout', testUndefinedOption, 1, 'final', 'webTransform', uppercaseBufferWebTransform().transform); -test('Cannot use "final" with webTransforms and stderr', testUndefinedOption, 2, 'final', 'webTransform', uppercaseBufferWebTransform().transform); -test('Cannot use "final" with webTransforms and stdio[*]', testUndefinedOption, 3, 'final', 'webTransform', uppercaseBufferWebTransform().transform); - -const testUndefinedFinal = (t, fdNumber, type, useTransform) => { - t.throws(() => { - execa('empty.js', getStdio(fdNumber, { - transform: useTransform ? uppercaseGenerator().transform : undefined, - final: generatorsMap[type].uppercase().transform, - })); - }, {message: type === 'duplex' ? /must not be a Duplex/ : /must not be a web TransformStream/}); -}; - -test('Cannot use "final" with duplexes and stdin, without transform', testUndefinedFinal, 0, 'duplex', false); -test('Cannot use "final" with duplexes and stdout, without transform', testUndefinedFinal, 1, 'duplex', false); -test('Cannot use "final" with duplexes and stderr, without transform', testUndefinedFinal, 2, 'duplex', false); -test('Cannot use "final" with duplexes and stdio[*], without transform', testUndefinedFinal, 3, 'duplex', false); -test('Cannot use "final" with duplexes and stdin, with transform', testUndefinedFinal, 0, 'duplex', true); -test('Cannot use "final" with duplexes and stdout, with transform', testUndefinedFinal, 1, 'duplex', true); -test('Cannot use "final" with duplexes and stderr, with transform', testUndefinedFinal, 2, 'duplex', true); -test('Cannot use "final" with duplexes and stdio[*], with transform', testUndefinedFinal, 3, 'duplex', true); -test('Cannot use "final" with webTransforms and stdin, without transform', testUndefinedFinal, 0, 'webTransform', false); -test('Cannot use "final" with webTransforms and stdout, without transform', testUndefinedFinal, 1, 'webTransform', false); -test('Cannot use "final" with webTransforms and stderr, without transform', testUndefinedFinal, 2, 'webTransform', false); -test('Cannot use "final" with webTransforms and stdio[*], without transform', testUndefinedFinal, 3, 'webTransform', false); -test('Cannot use "final" with webTransforms and stdin, with transform', testUndefinedFinal, 0, 'webTransform', true); -test('Cannot use "final" with webTransforms and stdout, with transform', testUndefinedFinal, 1, 'webTransform', true); -test('Cannot use "final" with webTransforms and stderr, with transform', testUndefinedFinal, 2, 'webTransform', true); -test('Cannot use "final" with webTransforms and stdio[*], with transform', testUndefinedFinal, 3, 'webTransform', true); - -const testSyncMethodsDuplex = (t, fdNumber, type) => { - t.throws(() => { - execaSync('empty.js', getStdio(fdNumber, generatorsMap[type].uppercase())); - }, {message: type === 'duplex' ? /cannot be a Duplex stream/ : /cannot be a web TransformStream/}); -}; - -test('Cannot use duplexes with sync methods and stdin', testSyncMethodsDuplex, 0, 'duplex'); -test('Cannot use duplexes with sync methods and stdout', testSyncMethodsDuplex, 1, 'duplex'); -test('Cannot use duplexes with sync methods and stderr', testSyncMethodsDuplex, 2, 'duplex'); -test('Cannot use duplexes with sync methods and stdio[*]', testSyncMethodsDuplex, 3, 'duplex'); -test('Cannot use webTransforms with sync methods and stdin', testSyncMethodsDuplex, 0, 'webTransform'); -test('Cannot use webTransforms with sync methods and stdout', testSyncMethodsDuplex, 1, 'webTransform'); -test('Cannot use webTransforms with sync methods and stderr', testSyncMethodsDuplex, 2, 'webTransform'); -test('Cannot use webTransforms with sync methods and stdio[*]', testSyncMethodsDuplex, 3, 'webTransform'); diff --git a/test/stdio/typed-array.js b/test/stdio/typed-array.js deleted file mode 100644 index 9f65aff43f..0000000000 --- a/test/stdio/typed-array.js +++ /dev/null @@ -1,34 +0,0 @@ -import test from 'ava'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {getStdio} from '../helpers/stdio.js'; -import {foobarUint8Array, foobarBuffer, foobarString} from '../helpers/input.js'; - -setFixtureDirectory(); - -const testUint8Array = async (t, fdNumber, stdioOption, execaMethod) => { - const {stdout} = await execaMethod('stdin-fd.js', [`${fdNumber}`], getStdio(fdNumber, stdioOption)); - t.is(stdout, foobarString); -}; - -test('stdin option can be a Uint8Array', testUint8Array, 0, foobarUint8Array, execa); -test('stdio[*] option can be a Uint8Array', testUint8Array, 3, foobarUint8Array, execa); -test('stdin option can be a Uint8Array - sync', testUint8Array, 0, foobarUint8Array, execaSync); -test('stdin option can be a Buffer', testUint8Array, 0, foobarBuffer, execa); -test('stdio[*] option can be a Buffer', testUint8Array, 3, foobarBuffer, execa); -test('stdin option can be a Buffer - sync', testUint8Array, 0, foobarBuffer, execaSync); - -const testNoUint8ArrayOutput = (t, fdNumber, stdioOption, execaMethod) => { - t.throws(() => { - execaMethod('empty.js', getStdio(fdNumber, stdioOption)); - }, {message: /cannot be a Uint8Array/}); -}; - -test('stdout option cannot be a Uint8Array', testNoUint8ArrayOutput, 1, foobarUint8Array, execa); -test('stderr option cannot be a Uint8Array', testNoUint8ArrayOutput, 2, foobarUint8Array, execa); -test('stdout option cannot be a Uint8Array - sync', testNoUint8ArrayOutput, 1, foobarUint8Array, execaSync); -test('stderr option cannot be a Uint8Array - sync', testNoUint8ArrayOutput, 2, foobarUint8Array, execaSync); -test('stdout option cannot be a Buffer', testNoUint8ArrayOutput, 1, foobarBuffer, execa); -test('stderr option cannot be a Buffer', testNoUint8ArrayOutput, 2, foobarBuffer, execa); -test('stdout option cannot be a Buffer - sync', testNoUint8ArrayOutput, 1, foobarBuffer, execaSync); -test('stderr option cannot be a Buffer - sync', testNoUint8ArrayOutput, 2, foobarBuffer, execaSync); diff --git a/test/stdio/web-stream.js b/test/stdio/web-stream.js deleted file mode 100644 index 567e0ca8e3..0000000000 --- a/test/stdio/web-stream.js +++ /dev/null @@ -1,98 +0,0 @@ -import {Readable} from 'node:stream'; -import {setImmediate} from 'node:timers/promises'; -import test from 'ava'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {getStdio} from '../helpers/stdio.js'; - -setFixtureDirectory(); - -const testReadableStream = async (t, fdNumber) => { - const readableStream = Readable.toWeb(Readable.from('foobar')); - const {stdout} = await execa('stdin-fd.js', [`${fdNumber}`], getStdio(fdNumber, readableStream)); - t.is(stdout, 'foobar'); -}; - -test('stdin can be a ReadableStream', testReadableStream, 0); -test('stdio[*] can be a ReadableStream', testReadableStream, 3); - -const testWritableStream = async (t, fdNumber) => { - const result = []; - const writableStream = new WritableStream({ - write(chunk) { - result.push(chunk); - }, - }); - await execa('noop-fd.js', [`${fdNumber}`, 'foobar'], getStdio(fdNumber, writableStream)); - t.is(result.join(''), 'foobar'); -}; - -test('stdout can be a WritableStream', testWritableStream, 1); -test('stderr can be a WritableStream', testWritableStream, 2); -test('stdio[*] can be a WritableStream', testWritableStream, 3); - -const testWebStreamSync = (t, StreamClass, fdNumber, optionName) => { - t.throws(() => { - execaSync('empty.js', getStdio(fdNumber, new StreamClass())); - }, {message: `The \`${optionName}\` option cannot be a web stream with synchronous methods.`}); -}; - -test('stdin cannot be a ReadableStream - sync', testWebStreamSync, ReadableStream, 0, 'stdin'); -test('stdio[*] cannot be a ReadableStream - sync', testWebStreamSync, ReadableStream, 3, 'stdio[3]'); -test('stdout cannot be a WritableStream - sync', testWebStreamSync, WritableStream, 1, 'stdout'); -test('stderr cannot be a WritableStream - sync', testWebStreamSync, WritableStream, 2, 'stderr'); -test('stdio[*] cannot be a WritableStream - sync', testWebStreamSync, WritableStream, 3, 'stdio[3]'); - -const testLongWritableStream = async (t, fdNumber) => { - let result = false; - const writableStream = new WritableStream({ - async close() { - await setImmediate(); - result = true; - }, - }); - await execa('empty.js', getStdio(fdNumber, writableStream)); - t.true(result); -}; - -test('stdout waits for WritableStream completion', testLongWritableStream, 1); -test('stderr waits for WritableStream completion', testLongWritableStream, 2); -test('stdio[*] waits for WritableStream completion', testLongWritableStream, 3); - -const testWritableStreamError = async (t, fdNumber) => { - const cause = new Error('foobar'); - const writableStream = new WritableStream({ - start(controller) { - controller.error(cause); - }, - }); - const error = await t.throwsAsync(execa('noop.js', getStdio(fdNumber, writableStream))); - t.is(error.cause, cause); -}; - -test('stdout option handles errors in WritableStream', testWritableStreamError, 1); -test('stderr option handles errors in WritableStream', testWritableStreamError, 2); -test('stdio[*] option handles errors in WritableStream', testWritableStreamError, 3); - -const testReadableStreamError = async (t, fdNumber) => { - const cause = new Error('foobar'); - const readableStream = new ReadableStream({ - start(controller) { - controller.error(cause); - }, - }); - const error = await t.throwsAsync(execa('stdin-fd.js', [`${fdNumber}`], getStdio(fdNumber, readableStream))); - t.is(error.cause, cause); -}; - -test('stdin option handles errors in ReadableStream', testReadableStreamError, 0); -test('stdio[*] option handles errors in ReadableStream', testReadableStreamError, 3); - -test('ReadableStream with stdin is canceled on subprocess exit', async t => { - let readableStream; - const promise = new Promise(resolve => { - readableStream = new ReadableStream({cancel: resolve}); - }); - await t.throwsAsync(execa('stdin.js', {stdin: readableStream, timeout: 1}), {message: /timed out/}); - await promise; -}); diff --git a/test/stdio/web-transform.js b/test/stdio/web-transform.js deleted file mode 100644 index 662c68c55a..0000000000 --- a/test/stdio/web-transform.js +++ /dev/null @@ -1,23 +0,0 @@ -import {promisify} from 'node:util'; -import {gunzip} from 'node:zlib'; -import test from 'ava'; -import {execa} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString, foobarUtf16Uint8Array, foobarUint8Array} from '../helpers/input.js'; - -setFixtureDirectory(); - -test('Can use CompressionStream()', async t => { - const {stdout} = await execa('noop-fd.js', ['1', foobarString], {stdout: new CompressionStream('gzip'), encoding: 'buffer'}); - const decompressedStdout = await promisify(gunzip)(stdout); - t.is(decompressedStdout.toString(), foobarString); -}); - -test('Can use TextDecoderStream()', async t => { - const {stdout} = await execa('stdin.js', { - input: foobarUtf16Uint8Array, - stdout: new TextDecoderStream('utf-16le'), - encoding: 'buffer', - }); - t.deepEqual(stdout, foobarUint8Array); -}); diff --git a/test/terminate/cancel.js b/test/terminate/cancel.js deleted file mode 100644 index 8ab68deca2..0000000000 --- a/test/terminate/cancel.js +++ /dev/null @@ -1,209 +0,0 @@ -import {once, getEventListeners} from 'node:events'; -import test from 'ava'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString} from '../helpers/input.js'; - -setFixtureDirectory(); - -const testValidCancelSignal = (t, cancelSignal) => { - t.throws(() => { - execa('empty.js', {cancelSignal}); - }, {message: /must be an AbortSignal/}); -}; - -test('cancelSignal option cannot be AbortController', testValidCancelSignal, new AbortController()); -test('cancelSignal option cannot be {}', testValidCancelSignal, {}); -test('cancelSignal option cannot be null', testValidCancelSignal, null); -test('cancelSignal option cannot be a symbol', testValidCancelSignal, Symbol('test')); - -test('result.isCanceled is false when abort isn\'t called (success)', async t => { - const {isCanceled, isGracefullyCanceled} = await execa('noop.js'); - t.false(isCanceled); - t.false(isGracefullyCanceled); -}); - -test('result.isCanceled is false when abort isn\'t called (failure)', async t => { - const {isCanceled, isGracefullyCanceled} = await t.throwsAsync(execa('fail.js')); - t.false(isCanceled); - t.false(isGracefullyCanceled); -}); - -test('result.isCanceled is false when abort isn\'t called in sync mode (success)', t => { - const {isCanceled, isGracefullyCanceled} = execaSync('noop.js'); - t.false(isCanceled); - t.false(isGracefullyCanceled); -}); - -test('result.isCanceled is false when abort isn\'t called in sync mode (failure)', t => { - const {isCanceled, isGracefullyCanceled} = t.throws(() => { - execaSync('fail.js'); - }); - t.false(isCanceled); - t.false(isGracefullyCanceled); -}); - -const testCancelSuccess = async (t, options) => { - const abortController = new AbortController(); - const subprocess = execa('noop.js', {cancelSignal: abortController.signal, ...options}); - abortController.abort(); - const {isCanceled, isGracefullyCanceled} = await t.throwsAsync(subprocess); - t.true(isCanceled); - t.false(isGracefullyCanceled); -}; - -test('error.isCanceled is true when abort is used', testCancelSuccess, {}); -test('gracefulCancel can be false with cancelSignal', testCancelSuccess, {gracefulCancel: false}); -test('ipc can be false with cancelSignal', testCancelSuccess, {ipc: false}); -test('serialization can be "json" with cancelSignal', testCancelSuccess, {ipc: true, serialization: 'json'}); - -test('error.isCanceled is false when kill method is used', async t => { - const abortController = new AbortController(); - const subprocess = execa('noop.js', {cancelSignal: abortController.signal}); - subprocess.kill(); - const {isCanceled, isGracefullyCanceled} = await t.throwsAsync(subprocess); - t.false(isCanceled); - t.false(isGracefullyCanceled); -}); - -test('calling abort is considered a signal termination', async t => { - const abortController = new AbortController(); - const subprocess = execa('forever.js', {cancelSignal: abortController.signal}); - await once(subprocess, 'spawn'); - abortController.abort(); - const {isCanceled, isGracefullyCanceled, isTerminated, signal} = await t.throwsAsync(subprocess); - t.true(isCanceled); - t.false(isGracefullyCanceled); - t.true(isTerminated); - t.is(signal, 'SIGTERM'); -}); - -test('cancelSignal can already be aborted', async t => { - const cancelSignal = AbortSignal.abort(); - const {isCanceled, isGracefullyCanceled, isTerminated, signal} = await t.throwsAsync(execa('forever.js', {cancelSignal})); - t.true(isCanceled); - t.false(isGracefullyCanceled); - t.true(isTerminated); - t.is(signal, 'SIGTERM'); - t.deepEqual(getEventListeners(cancelSignal, 'abort'), []); -}); - -test('calling abort does not emit the "error" event', async t => { - const abortController = new AbortController(); - const subprocess = execa('forever.js', {cancelSignal: abortController.signal}); - let error; - subprocess.once('error', errorArgument => { - error = errorArgument; - }); - abortController.abort(); - const {isCanceled, isGracefullyCanceled} = await t.throwsAsync(subprocess); - t.true(isCanceled); - t.false(isGracefullyCanceled); - t.is(error, undefined); -}); - -test('calling abort cleans up listeners on cancelSignal, called', async t => { - const abortController = new AbortController(); - const subprocess = execa('forever.js', {cancelSignal: abortController.signal}); - t.is(getEventListeners(abortController.signal, 'abort').length, 1); - abortController.abort(); - const {isCanceled, isGracefullyCanceled} = await t.throwsAsync(subprocess); - t.true(isCanceled); - t.false(isGracefullyCanceled); - t.is(getEventListeners(abortController.signal, 'abort').length, 0); -}); - -test('calling abort cleans up listeners on cancelSignal, not called', async t => { - const abortController = new AbortController(); - const subprocess = execa('noop.js', {cancelSignal: abortController.signal}); - t.is(getEventListeners(abortController.signal, 'abort').length, 1); - await subprocess; - t.is(getEventListeners(abortController.signal, 'abort').length, 0); -}); - -test('calling abort cleans up listeners on cancelSignal, already aborted', async t => { - const cancelSignal = AbortSignal.abort(); - const subprocess = execa('noop.js', {cancelSignal}); - t.is(getEventListeners(cancelSignal, 'abort').length, 0); - const {isCanceled, isGracefullyCanceled} = await t.throwsAsync(subprocess); - t.true(isCanceled); - t.false(isGracefullyCanceled); - t.is(getEventListeners(cancelSignal, 'abort').length, 0); -}); - -test('calling abort throws an error with message "Command was canceled"', async t => { - const abortController = new AbortController(); - const subprocess = execa('noop.js', {cancelSignal: abortController.signal}); - abortController.abort(); - await t.throwsAsync(subprocess, {message: /Command was canceled/}); -}); - -test('calling abort with no argument keeps error properties', async t => { - const abortController = new AbortController(); - const subprocess = execa('empty.js', {cancelSignal: abortController.signal}); - abortController.abort(); - const {cause, originalMessage, shortMessage, message} = await t.throwsAsync(subprocess); - t.is(cause.message, 'This operation was aborted'); - t.is(cause.name, 'AbortError'); - t.is(originalMessage, 'This operation was aborted'); - t.is(shortMessage, 'Command was canceled: empty.js\nThis operation was aborted'); - t.is(message, 'Command was canceled: empty.js\nThis operation was aborted'); -}); - -test('calling abort with an error instance keeps error properties', async t => { - const abortController = new AbortController(); - const subprocess = execa('empty.js', {cancelSignal: abortController.signal}); - const error = new Error(foobarString); - error.code = foobarString; - abortController.abort(error); - const {cause, originalMessage, shortMessage, message, code} = await t.throwsAsync(subprocess); - t.is(cause, error); - t.is(originalMessage, foobarString); - t.is(shortMessage, `Command was canceled: empty.js\n${foobarString}`); - t.is(message, `Command was canceled: empty.js\n${foobarString}`); - t.is(code, foobarString); -}); - -test('calling abort with null keeps error properties', async t => { - const abortController = new AbortController(); - const subprocess = execa('empty.js', {cancelSignal: abortController.signal}); - abortController.abort(null); - const {cause, originalMessage, shortMessage, message} = await t.throwsAsync(subprocess); - t.is(cause, null); - t.is(originalMessage, 'null'); - t.is(shortMessage, 'Command was canceled: empty.js\nnull'); - t.is(message, 'Command was canceled: empty.js\nnull'); -}); - -test('calling abort twice should show the same behaviour as calling it once', async t => { - const abortController = new AbortController(); - const subprocess = execa('noop.js', {cancelSignal: abortController.signal}); - abortController.abort(); - abortController.abort(); - const {isCanceled, isGracefullyCanceled} = await t.throwsAsync(subprocess); - t.true(isCanceled); - t.false(isGracefullyCanceled); -}); - -test('calling abort on a successfully completed subprocess does not make result.isCanceled true', async t => { - const abortController = new AbortController(); - const subprocess = execa('noop.js', {cancelSignal: abortController.signal}); - const {isCanceled, isGracefullyCanceled} = await subprocess; - abortController.abort(); - t.false(isCanceled); - t.false(isGracefullyCanceled); -}); - -test('Throws when using the former "signal" option name', t => { - const abortController = new AbortController(); - t.throws(() => { - execa('empty.js', {signal: abortController.signal}); - }, {message: /renamed to "cancelSignal"/}); -}); - -test('Cannot use cancelSignal, sync', t => { - const abortController = new AbortController(); - t.throws(() => { - execaSync('empty.js', {cancelSignal: abortController.signal}); - }, {message: /The "cancelSignal" option cannot be used/}); -}); diff --git a/test/terminate/cleanup.js b/test/terminate/cleanup.js deleted file mode 100644 index 613902ed25..0000000000 --- a/test/terminate/cleanup.js +++ /dev/null @@ -1,101 +0,0 @@ -import process from 'node:process'; -import {setTimeout} from 'node:timers/promises'; -import test from 'ava'; -import isRunning from 'is-running'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {nestedSubprocess} from '../helpers/nested.js'; -import {foobarString} from '../helpers/input.js'; - -setFixtureDirectory(); - -const isWindows = process.platform === 'win32'; - -// When subprocess exits before current process -const spawnAndExit = async (t, worker, cleanup, detached) => { - const {nestedResult: {stdout}} = await nestedSubprocess('noop-fd.js', ['1', foobarString], {worker, cleanup, detached}); - t.is(stdout, foobarString); -}; - -test('spawnAndExit', spawnAndExit, false, false, false); -test('spawnAndExit cleanup', spawnAndExit, false, true, false); -test('spawnAndExit detached', spawnAndExit, false, false, true); -test('spawnAndExit cleanup detached', spawnAndExit, false, true, true); -test('spawnAndExit, worker', spawnAndExit, true, false, false); -test('spawnAndExit cleanup, worker', spawnAndExit, true, true, false); -test('spawnAndExit detached, worker', spawnAndExit, true, false, true); -test('spawnAndExit cleanup detached, worker', spawnAndExit, true, true, true); - -// When current process exits before subprocess -const spawnAndKill = async (t, [signal, cleanup, detached, isKilled]) => { - const subprocess = execa('ipc-send-pid.js', [cleanup, detached], {stdio: 'ignore', ipc: true}); - - const pid = await subprocess.getOneMessage(); - t.true(Number.isInteger(pid)); - t.true(isRunning(pid)); - - process.kill(subprocess.pid, signal); - - await t.throwsAsync(subprocess); - t.false(isRunning(subprocess.pid)); - - if (isKilled) { - await Promise.race([ - setTimeout(1e4, undefined, {ref: false}), - pollForSubprocessExit(pid), - ]); - t.is(isRunning(pid), false); - } else { - t.is(isRunning(pid), true); - process.kill(pid, 'SIGKILL'); - } -}; - -const pollForSubprocessExit = async pid => { - while (isRunning(pid)) { - // eslint-disable-next-line no-await-in-loop - await setTimeout(100); - } -}; - -// Without `options.cleanup`: -// - on Windows subprocesses are killed if `options.detached: false`, but not -// if `options.detached: true` -// - on Linux subprocesses are never killed regardless of `options.detached` -// With `options.cleanup`, subprocesses are always killed -// - `options.cleanup` with SIGKILL is a noop, since it cannot be handled -test('spawnAndKill SIGTERM', spawnAndKill, ['SIGTERM', false, false, isWindows]); -test('spawnAndKill SIGKILL', spawnAndKill, ['SIGKILL', false, false, isWindows]); -test('spawnAndKill cleanup SIGTERM', spawnAndKill, ['SIGTERM', true, false, true]); -test('spawnAndKill cleanup SIGKILL', spawnAndKill, ['SIGKILL', true, false, isWindows]); -test('spawnAndKill detached SIGTERM', spawnAndKill, ['SIGTERM', false, true, false]); -test('spawnAndKill detached SIGKILL', spawnAndKill, ['SIGKILL', false, true, false]); -test('spawnAndKill cleanup detached SIGTERM', spawnAndKill, ['SIGTERM', true, true, false]); -test('spawnAndKill cleanup detached SIGKILL', spawnAndKill, ['SIGKILL', true, true, false]); - -// See #128 -test('removes exit handler on exit', async t => { - // @todo this relies on `signal-exit` internals - const exitListeners = globalThis[Symbol.for('signal-exit emitter')].listeners.exit; - - const subprocess = execa('noop.js'); - const listener = exitListeners.at(-1); - - await subprocess; - t.false(exitListeners.includes(listener)); -}); - -test('detach subprocess', async t => { - const {stdout} = await execa('detach.js'); - const pid = Number(stdout); - t.true(Number.isInteger(pid)); - t.true(isRunning(pid)); - - process.kill(pid, 'SIGKILL'); -}); - -test('Cannot use "detached" option, sync', t => { - t.throws(() => { - execaSync('empty.js', {detached: true}); - }, {message: /The "detached: true" option cannot be used/}); -}); diff --git a/test/terminate/graceful.js b/test/terminate/graceful.js deleted file mode 100644 index 6ab4429e84..0000000000 --- a/test/terminate/graceful.js +++ /dev/null @@ -1,178 +0,0 @@ -import {setTimeout} from 'node:timers/promises'; -import test from 'ava'; -import {execa} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString} from '../helpers/input.js'; -import {mockSendIoError} from '../helpers/ipc.js'; - -setFixtureDirectory(); - -test('cancelSignal cannot be undefined with gracefulCancel', t => { - t.throws(() => { - execa('empty.js', {gracefulCancel: true}); - }, {message: /The `cancelSignal` option must be defined/}); -}); - -test('ipc cannot be false with gracefulCancel', t => { - t.throws(() => { - execa('empty.js', {gracefulCancel: true, cancelSignal: AbortSignal.abort(), ipc: false}); - }, {message: /The `ipc` option cannot be false/}); -}); - -test('serialization cannot be "json" with gracefulCancel', t => { - t.throws(() => { - execa('empty.js', {gracefulCancel: true, cancelSignal: AbortSignal.abort(), serialization: 'json'}); - }, {message: /The `serialization` option cannot be 'json'/}); -}); - -test('Current process can send a message right away', async t => { - const controller = new AbortController(); - const subprocess = execa('ipc-echo.js', {cancelSignal: controller.signal, gracefulCancel: true}); - await subprocess.sendMessage(foobarString); - const {ipcOutput} = await subprocess; - t.deepEqual(ipcOutput, [foobarString]); -}); - -test('Current process can receive a message right away', async t => { - const controller = new AbortController(); - const subprocess = execa('ipc-send.js', {cancelSignal: controller.signal, gracefulCancel: true}); - t.is(await subprocess.getOneMessage(), foobarString); - const {ipcOutput} = await subprocess; - t.deepEqual(ipcOutput, [foobarString]); -}); - -test('Does not disconnect during I/O errors when sending the abort reason', async t => { - const controller = new AbortController(); - const subprocess = execa('ipc-echo.js', {cancelSignal: controller.signal, gracefulCancel: true, forceKillAfterDelay: false}); - const error = mockSendIoError(subprocess); - controller.abort(foobarString); - await setTimeout(0); - t.true(subprocess.connected); - subprocess.kill(); - const {isCanceled, isGracefullyCanceled, signal, ipcOutput, cause} = await t.throwsAsync(subprocess); - t.false(isCanceled); - t.false(isGracefullyCanceled); - t.is(signal, 'SIGTERM'); - t.deepEqual(ipcOutput, []); - t.is(cause, error); -}); - -class AbortError extends Error { - name = 'AbortError'; -} - -test('Abort reason is sent to the subprocess', async t => { - const controller = new AbortController(); - const subprocess = execa('graceful-send.js', {cancelSignal: controller.signal, gracefulCancel: true, forceKillAfterDelay: false}); - const error = new AbortError(foobarString); - controller.abort(error); - const {isCanceled, isGracefullyCanceled, isTerminated, exitCode, cause, ipcOutput} = await t.throwsAsync(subprocess); - t.true(isCanceled); - t.true(isGracefullyCanceled); - t.false(isTerminated); - t.is(exitCode, 0); - t.is(cause, error); - t.is(ipcOutput[0].message, error.message); - t.is(ipcOutput[0].stack, error.stack); - t.is(ipcOutput[0].name, 'Error'); -}); - -test('Abort default reason is sent to the subprocess', async t => { - const controller = new AbortController(); - const subprocess = execa('graceful-send.js', {cancelSignal: controller.signal, gracefulCancel: true, forceKillAfterDelay: false}); - controller.abort(); - const {isCanceled, isGracefullyCanceled, isTerminated, exitCode, cause, ipcOutput} = await t.throwsAsync(subprocess); - t.true(isCanceled); - t.true(isGracefullyCanceled); - t.false(isTerminated); - t.is(exitCode, 0); - const {reason} = controller.signal; - t.is(cause.stack, reason.stack); - t.is(ipcOutput[0].message, reason.message); - t.is(ipcOutput[0].stack, reason.stack); -}); - -test('Fail when sending non-serializable abort reason', async t => { - const controller = new AbortController(); - const subprocess = execa('ipc-echo.js', {cancelSignal: controller.signal, gracefulCancel: true, forceKillAfterDelay: false}); - controller.abort(() => {}); - await setTimeout(0); - t.true(subprocess.connected); - await subprocess.sendMessage(foobarString); - const {isCanceled, isGracefullyCanceled, isTerminated, exitCode, cause, ipcOutput} = await t.throwsAsync(subprocess); - t.false(isCanceled); - t.false(isGracefullyCanceled); - t.false(isTerminated); - t.is(exitCode, 0); - t.deepEqual(ipcOutput, [foobarString]); - t.is(cause.message, '`cancelSignal`\'s `controller.abort()`\'s argument type is invalid: the message cannot be serialized: () => {}.'); - t.is(cause.cause.message, '() => {} could not be cloned.'); -}); - -test('timeout does not use graceful cancelSignal', async t => { - const controller = new AbortController(); - const {timedOut, isCanceled, isGracefullyCanceled, isTerminated, signal, exitCode, shortMessage, ipcOutput} = await t.throwsAsync(execa('graceful-send.js', {cancelSignal: controller.signal, gracefulCancel: true, timeout: 1})); - t.true(timedOut); - t.false(isCanceled); - t.false(isGracefullyCanceled); - t.true(isTerminated); - t.is(signal, 'SIGTERM'); - t.is(exitCode, undefined); - t.is(shortMessage, 'Command timed out after 1 milliseconds: graceful-send.js'); - t.deepEqual(ipcOutput, []); -}); - -test('error on graceful cancelSignal on non-0 exit code', async t => { - const {isCanceled, isGracefullyCanceled, isTerminated, isForcefullyTerminated, exitCode, shortMessage} = await t.throwsAsync(execa('wait-fail.js', {cancelSignal: AbortSignal.abort(''), gracefulCancel: true, forceKillAfterDelay: false})); - t.true(isCanceled); - t.true(isGracefullyCanceled); - t.false(isTerminated); - t.false(isForcefullyTerminated); - t.is(exitCode, 2); - t.is(shortMessage, 'Command was gracefully canceled with exit code 2: wait-fail.js'); -}); - -test('error on graceful cancelSignal on forceful termination', async t => { - const {isCanceled, isGracefullyCanceled, isTerminated, signal, isForcefullyTerminated, exitCode, shortMessage} = await t.throwsAsync(execa('forever.js', {cancelSignal: AbortSignal.abort(''), gracefulCancel: true, forceKillAfterDelay: 1})); - t.true(isCanceled); - t.true(isGracefullyCanceled); - t.true(isTerminated); - t.is(signal, 'SIGKILL'); - t.true(isForcefullyTerminated); - t.is(exitCode, undefined); - t.is(shortMessage, 'Command was gracefully canceled and was forcefully terminated after 1 milliseconds: forever.js'); -}); - -test('error on graceful cancelSignal on non-forceful termination', async t => { - const subprocess = execa('ipc-send-get.js', {cancelSignal: AbortSignal.abort(''), gracefulCancel: true, forceKillAfterDelay: 1e6}); - t.is(await subprocess.getOneMessage(), foobarString); - subprocess.kill(); - const {isCanceled, isGracefullyCanceled, isTerminated, signal, isForcefullyTerminated, exitCode, shortMessage} = await t.throwsAsync(subprocess); - t.true(isCanceled); - t.true(isGracefullyCanceled); - t.true(isTerminated); - t.is(signal, 'SIGTERM'); - t.false(isForcefullyTerminated); - t.is(exitCode, undefined); - t.is(shortMessage, 'Command was gracefully canceled with SIGTERM (Termination): ipc-send-get.js'); -}); - -test('`forceKillAfterDelay: false` with the "cancelSignal" option when graceful', async t => { - const subprocess = execa('forever.js', {cancelSignal: AbortSignal.abort(''), gracefulCancel: true, forceKillAfterDelay: false}); - await setTimeout(6e3); - subprocess.kill('SIGKILL'); - const {isCanceled, isGracefullyCanceled, isTerminated, signal, isForcefullyTerminated, exitCode, shortMessage} = await t.throwsAsync(subprocess); - t.true(isCanceled); - t.true(isGracefullyCanceled); - t.true(isTerminated); - t.is(signal, 'SIGKILL'); - t.false(isForcefullyTerminated); - t.is(exitCode, undefined); - t.is(shortMessage, 'Command was gracefully canceled with SIGKILL (Forced termination): forever.js'); -}); - -test('subprocess.getCancelSignal() is not defined', async t => { - const subprocess = execa('empty.js', {cancelSignal: AbortSignal.abort(''), gracefulCancel: true}); - t.is(subprocess.getCancelSignal, undefined); - await t.throwsAsync(subprocess); -}); diff --git a/test/terminate/kill-error.js b/test/terminate/kill-error.js deleted file mode 100644 index 6bd7bbd936..0000000000 --- a/test/terminate/kill-error.js +++ /dev/null @@ -1,83 +0,0 @@ -import {once} from 'node:events'; -import {setImmediate} from 'node:timers/promises'; -import test from 'ava'; -import isRunning from 'is-running'; -import {execa} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; - -setFixtureDirectory(); - -test('.kill(error) propagates error', async t => { - const subprocess = execa('forever.js'); - const originalMessage = 'test'; - const cause = new Error(originalMessage); - t.true(subprocess.kill(cause)); - const error = await t.throwsAsync(subprocess); - t.is(error.cause, cause); - t.true(cause.stack.includes(import.meta.url)); - t.is(error.exitCode, undefined); - t.is(error.signal, 'SIGTERM'); - t.true(error.isTerminated); - t.is(error.originalMessage, originalMessage); - t.true(error.message.includes(originalMessage)); - t.true(error.message.includes('was killed with SIGTERM')); -}); - -test('.kill(error) uses killSignal', async t => { - const subprocess = execa('forever.js', {killSignal: 'SIGINT'}); - const cause = new Error('test'); - subprocess.kill(cause); - const error = await t.throwsAsync(subprocess); - t.is(error.cause, cause); - t.is(error.signal, 'SIGINT'); -}); - -test('.kill(signal, error) uses signal', async t => { - const subprocess = execa('forever.js'); - const cause = new Error('test'); - subprocess.kill('SIGINT', cause); - const error = await t.throwsAsync(subprocess); - t.is(error.cause, cause); - t.is(error.signal, 'SIGINT'); -}); - -test('.kill(error) is a noop if subprocess already exited', async t => { - const subprocess = execa('empty.js'); - await subprocess; - t.false(isRunning(subprocess.pid)); - t.false(subprocess.kill(new Error('test'))); -}); - -test('.kill(error) terminates but does not change the error if the subprocess already errored but did not exit yet', async t => { - const subprocess = execa('forever.js'); - const cause = new Error('first'); - subprocess.stdout.destroy(cause); - await setImmediate(); - const secondError = new Error('second'); - t.true(subprocess.kill(secondError)); - const error = await t.throwsAsync(subprocess); - t.is(error.cause, cause); - t.is(error.exitCode, undefined); - t.is(error.signal, 'SIGTERM'); - t.true(error.isTerminated); - t.false(error.message.includes(secondError.message)); -}); - -test('.kill(error) twice in a row', async t => { - const subprocess = execa('forever.js'); - const cause = new Error('first'); - subprocess.kill(cause); - const secondCause = new Error('second'); - subprocess.kill(secondCause); - const error = await t.throwsAsync(subprocess); - t.is(error.cause, cause); - t.false(error.message.includes(secondCause.message)); -}); - -test('.kill(error) does not emit the "error" event', async t => { - const subprocess = execa('forever.js'); - const cause = new Error('test'); - subprocess.kill(cause); - const error = await Promise.race([t.throwsAsync(subprocess), once(subprocess, 'error')]); - t.is(error.cause, cause); -}); diff --git a/test/terminate/kill-force.js b/test/terminate/kill-force.js deleted file mode 100644 index 8b4e36748f..0000000000 --- a/test/terminate/kill-force.js +++ /dev/null @@ -1,218 +0,0 @@ -import process from 'node:process'; -import {once, defaultMaxListeners} from 'node:events'; -import {constants} from 'node:os'; -import {setTimeout} from 'node:timers/promises'; -import test from 'ava'; -import isRunning from 'is-running'; -import {execa} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {assertMaxListeners} from '../helpers/listeners.js'; -import {foobarString} from '../helpers/input.js'; -import {getEarlyErrorSubprocess} from '../helpers/early-error.js'; - -setFixtureDirectory(); - -const isWindows = process.platform === 'win32'; - -const spawnNoKillable = async (forceKillAfterDelay, options) => { - const subprocess = execa('no-killable.js', { - ipc: true, - forceKillAfterDelay, - ...options, - }); - await subprocess.getOneMessage(); - return {subprocess}; -}; - -const noKillableSimpleOptions = {killSignal: 'SIGWINCH', forceKillAfterDelay: 1}; -const spawnNoKillableSimple = options => execa('forever.js', {...noKillableSimpleOptions, ...options}); - -test('kill("SIGKILL") should terminate cleanly', async t => { - const {subprocess} = await spawnNoKillable(); - - subprocess.kill('SIGKILL'); - - const {isTerminated, signal, isForcefullyTerminated, shortMessage} = await t.throwsAsync(subprocess); - t.true(isTerminated); - t.is(signal, 'SIGKILL'); - t.false(isForcefullyTerminated); - t.is(shortMessage, 'Command was killed with SIGKILL (Forced termination): no-killable.js'); -}); - -const testInvalidForceKill = async (t, forceKillAfterDelay) => { - t.throws(() => { - execa('empty.js', {forceKillAfterDelay}); - }, {instanceOf: TypeError, message: /non-negative integer/}); -}; - -test('`forceKillAfterDelay` should not be NaN', testInvalidForceKill, Number.NaN); -test('`forceKillAfterDelay` should not be negative', testInvalidForceKill, -1); - -// `SIGTERM` cannot be caught on Windows, and it always aborts the subprocess (like `SIGKILL` on Unix). -// Therefore, this feature and those tests must be different on Windows. -if (isWindows) { - test('Can call `.kill()` with `forceKillAfterDelay` on Windows', async t => { - const {subprocess} = await spawnNoKillable(); - subprocess.kill(); - - const {isTerminated, signal, isForcefullyTerminated, shortMessage} = await t.throwsAsync(subprocess); - t.true(isTerminated); - t.is(signal, 'SIGTERM'); - t.false(isForcefullyTerminated); - t.is(shortMessage, 'Command was killed with SIGTERM (Termination): no-killable.js'); - }); -} else { - const testNoForceKill = async (t, forceKillAfterDelay, killArgument, options) => { - const {subprocess} = await spawnNoKillable(forceKillAfterDelay, options); - - subprocess.kill(killArgument); - - await setTimeout(6e3); - t.true(isRunning(subprocess.pid)); - subprocess.kill('SIGKILL'); - - const {isTerminated, signal, isForcefullyTerminated} = await t.throwsAsync(subprocess); - t.true(isTerminated); - t.is(signal, 'SIGKILL'); - t.false(isForcefullyTerminated); - }; - - test('`forceKillAfterDelay: false` should not kill after a timeout', testNoForceKill, false); - test('`forceKillAfterDelay` should not kill after a timeout with other signals', testNoForceKill, true, 'SIGINT'); - test('`forceKillAfterDelay` should not kill after a timeout with wrong killSignal string', testNoForceKill, true, 'SIGTERM', {killSignal: 'SIGINT'}); - test('`forceKillAfterDelay` should not kill after a timeout with wrong killSignal number', testNoForceKill, true, constants.signals.SIGTERM, {killSignal: constants.signals.SIGINT}); - - // eslint-disable-next-line max-params - const testForceKill = async (t, forceKillAfterDelay, killSignal, expectedDelay, expectedKillSignal, options) => { - const {subprocess} = await spawnNoKillable(forceKillAfterDelay, options); - - subprocess.kill(killSignal); - - const {isTerminated, signal, isForcefullyTerminated, shortMessage} = await t.throwsAsync(subprocess); - t.true(isTerminated); - t.is(signal, 'SIGKILL'); - t.true(isForcefullyTerminated); - const messageSuffix = killSignal instanceof Error ? `\n${killSignal.message}` : ''; - const signalDescription = expectedKillSignal === 'SIGINT' ? 'User interruption with CTRL-C' : 'Termination'; - t.is(shortMessage, `Command was killed with ${expectedKillSignal} (${signalDescription}) and was forcefully terminated after ${expectedDelay} milliseconds: no-killable.js${messageSuffix}`); - }; - - test('`forceKillAfterDelay: number` should kill after a timeout', testForceKill, 50, undefined, 50, 'SIGTERM'); - test('`forceKillAfterDelay: true` should kill after a timeout', testForceKill, true, undefined, 5e3, 'SIGTERM'); - test('`forceKillAfterDelay: undefined` should kill after a timeout', testForceKill, undefined, undefined, 5e3, 'SIGTERM'); - test('`forceKillAfterDelay` should kill after a timeout with SIGTERM', testForceKill, 50, 'SIGTERM', 50, 'SIGTERM'); - test('`forceKillAfterDelay` should kill after a timeout with the killSignal string', testForceKill, 50, 'SIGINT', 50, 'SIGINT', {killSignal: 'SIGINT'}); - test('`forceKillAfterDelay` should kill after a timeout with the killSignal string, mixed', testForceKill, 50, 'SIGINT', 50, 'SIGINT', {killSignal: constants.signals.SIGINT}); - test('`forceKillAfterDelay` should kill after a timeout with the killSignal number', testForceKill, 50, constants.signals.SIGINT, 50, 'SIGINT', {killSignal: constants.signals.SIGINT}); - test('`forceKillAfterDelay` should kill after a timeout with the killSignal number, mixed', testForceKill, 50, constants.signals.SIGINT, 50, 'SIGINT', {killSignal: 'SIGINT'}); - test('`forceKillAfterDelay` should kill after a timeout with an error', testForceKill, 50, new Error('test'), 50, 'SIGTERM'); - test('`forceKillAfterDelay` should kill after a timeout with an error and a killSignal', testForceKill, 50, new Error('test'), 50, 'SIGINT', {killSignal: 'SIGINT'}); - - test('`forceKillAfterDelay` works with the "cancelSignal" option', async t => { - const abortController = new AbortController(); - const subprocess = spawnNoKillableSimple({cancelSignal: abortController.signal}); - await once(subprocess, 'spawn'); - abortController.abort(''); - const {isTerminated, signal, isCanceled, isGracefullyCanceled, isForcefullyTerminated, shortMessage} = await t.throwsAsync(subprocess); - t.true(isTerminated); - t.is(signal, 'SIGKILL'); - t.true(isCanceled); - t.false(isGracefullyCanceled); - t.true(isForcefullyTerminated); - t.is(shortMessage, 'Command was canceled and was forcefully terminated after 1 milliseconds: forever.js'); - }); - - test('`forceKillAfterDelay` works with the "timeout" option', async t => { - const {isTerminated, signal, timedOut, isForcefullyTerminated, shortMessage} = await t.throwsAsync(spawnNoKillableSimple({timeout: 1})); - t.true(isTerminated); - t.is(signal, 'SIGKILL'); - t.true(timedOut); - t.true(isForcefullyTerminated); - t.is(shortMessage, 'Command timed out after 1 milliseconds and was forcefully terminated after 1 milliseconds: forever.js'); - }); - - test('`forceKillAfterDelay` works with the "maxBuffer" option', async t => { - const subprocess = execa('noop-forever.js', ['.'], {...noKillableSimpleOptions, maxBuffer: 1}); - const [chunk] = await once(subprocess.stdout, 'data'); - t.is(chunk.toString(), '.\n'); - subprocess.kill(); - const {isTerminated, signal, isForcefullyTerminated, shortMessage} = await t.throwsAsync(subprocess); - t.true(isTerminated); - t.is(signal, 'SIGKILL'); - t.true(isForcefullyTerminated); - t.is(shortMessage, 'Command\'s stdout was larger than 1 characters and was forcefully terminated after 1 milliseconds: noop-forever.js .\nmaxBuffer exceeded'); - }); - - test('`forceKillAfterDelay` works with the "error" event', async t => { - const subprocess = spawnNoKillableSimple(); - await once(subprocess, 'spawn'); - const error = new Error(foobarString); - error.code = 'ECODE'; - subprocess.emit('error', error); - subprocess.kill(); - const {isTerminated, signal, isForcefullyTerminated, shortMessage, originalMessage, cause} = await t.throwsAsync(subprocess); - t.true(isTerminated); - t.is(signal, 'SIGKILL'); - t.true(isForcefullyTerminated); - t.is(cause, error); - t.is(originalMessage, error.message); - t.is(shortMessage, `Command failed with ${error.code} and was forcefully terminated after 1 milliseconds: forever.js\n${error.message}`); - }); - - test.serial('Can call `.kill()` with `forceKillAfterDelay` many times without triggering the maxListeners warning', async t => { - const checkMaxListeners = assertMaxListeners(t); - - const subprocess = spawnNoKillableSimple(); - for (let index = 0; index < defaultMaxListeners + 1; index += 1) { - subprocess.kill(); - } - - const {isTerminated, signal, isForcefullyTerminated} = await t.throwsAsync(subprocess); - t.true(isTerminated); - t.is(signal, 'SIGKILL'); - t.true(isForcefullyTerminated); - - checkMaxListeners(); - }); - - test('Can call `.kill()` with `forceKillAfterDelay` multiple times', async t => { - const subprocess = spawnNoKillableSimple(); - subprocess.kill(); - subprocess.kill(); - - const {isTerminated, signal, isForcefullyTerminated} = await t.throwsAsync(subprocess); - t.true(isTerminated); - t.is(signal, 'SIGKILL'); - t.true(isForcefullyTerminated); - }); -} - -test('result.isForcefullyTerminated is false on success', async t => { - const {isForcefullyTerminated} = await execa('empty.js'); - t.false(isForcefullyTerminated); -}); - -test('error.isForcefullyTerminated is false on spawn errors', async t => { - const {isForcefullyTerminated} = await t.throwsAsync(getEarlyErrorSubprocess()); - t.false(isForcefullyTerminated); -}); - -test('error.isForcefullyTerminated is false when already terminated', async t => { - const abortController = new AbortController(); - const final = async function * () { - try { - await setTimeout(1e6, undefined, {signal: abortController.signal}); - } catch {} - - yield * []; - }; - - const subprocess = execa('forever.js', {stdout: {final}}); - subprocess.kill(); - await setTimeout(6e3); - abortController.abort(); - const {isForcefullyTerminated, isTerminated, signal} = await t.throwsAsync(subprocess); - t.false(isForcefullyTerminated); - t.true(isTerminated); - t.is(signal, 'SIGTERM'); -}); diff --git a/test/terminate/kill-signal.js b/test/terminate/kill-signal.js deleted file mode 100644 index c3647d6dd6..0000000000 --- a/test/terminate/kill-signal.js +++ /dev/null @@ -1,155 +0,0 @@ -import {once} from 'node:events'; -import {platform, version} from 'node:process'; -import {constants} from 'node:os'; -import {setImmediate} from 'node:timers/promises'; -import test from 'ava'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; - -setFixtureDirectory(); - -const isWindows = platform === 'win32'; -const majorNodeVersion = Number(version.split('.')[0].slice(1)); - -const testKillSignal = async (t, killSignal) => { - const {isTerminated, signal} = await t.throwsAsync(execa('forever.js', {killSignal, timeout: 1})); - t.true(isTerminated); - t.is(signal, 'SIGINT'); -}; - -test('Can use killSignal: "SIGINT"', testKillSignal, 'SIGINT'); -test('Can use killSignal: 2', testKillSignal, constants.signals.SIGINT); - -const testKillSignalSync = (t, killSignal) => { - const {isTerminated, signal} = t.throws(() => { - execaSync('forever.js', {killSignal, timeout: 1}); - }); - t.true(isTerminated); - t.is(signal, 'SIGINT'); -}; - -test('Can use killSignal: "SIGINT", sync', testKillSignalSync, 'SIGINT'); -test('Can use killSignal: 2, sync', testKillSignalSync, constants.signals.SIGINT); - -test('Can call .kill("SIGTERM")', async t => { - const subprocess = execa('forever.js'); - subprocess.kill('SIGTERM'); - const {isTerminated, signal} = await t.throwsAsync(subprocess); - t.true(isTerminated); - t.is(signal, 'SIGTERM'); -}); - -test('Can call .kill(15)', async t => { - const subprocess = execa('forever.js'); - subprocess.kill(constants.signals.SIGTERM); - const {isTerminated, signal} = await t.throwsAsync(subprocess); - t.true(isTerminated); - t.is(signal, 'SIGTERM'); -}); - -test('Can call .kill(0)', async t => { - const subprocess = execa('forever.js'); - t.true(subprocess.kill(0)); - subprocess.kill(); - await t.throwsAsync(subprocess); - t.false(subprocess.kill(0)); -}); - -test('Can call `.kill()` multiple times', async t => { - const subprocess = execa('forever.js'); - subprocess.kill(); - subprocess.kill(); - - const {exitCode, isTerminated, signal, code} = await t.throwsAsync(subprocess); - - // On Windows, calling `subprocess.kill()` twice emits an `error` event on the subprocess. - // This does not happen when passing an `error` argument, nor when passing a non-terminating signal. - // There is no easy way to make this cross-platform, so we document the difference here. - if (isWindows && majorNodeVersion >= 22) { - t.is(exitCode, undefined); - t.false(isTerminated); - t.is(signal, undefined); - t.is(code, 'EPERM'); - } else { - t.is(exitCode, undefined); - t.true(isTerminated); - t.is(signal, 'SIGTERM'); - t.is(code, undefined); - } -}); - -test('execa() returns a promise with kill()', async t => { - const subprocess = execa('noop.js', ['foo']); - t.is(typeof subprocess.kill, 'function'); - await subprocess; -}); - -const testInvalidKillArgument = async (t, killArgument, secondKillArgument) => { - const subprocess = execa('empty.js'); - const message = secondKillArgument instanceof Error || secondKillArgument === undefined - ? /error instance or a signal name/ - : /second argument is optional/; - t.throws(() => { - subprocess.kill(killArgument, secondKillArgument); - }, {message}); - await subprocess; -}; - -test('Cannot call .kill(errorObject)', testInvalidKillArgument, {name: '', message: '', stack: ''}); -test('Cannot call .kill(errorArray)', testInvalidKillArgument, [new Error('test')]); -test('Cannot call .kill(undefined, true)', testInvalidKillArgument, undefined, true); -test('Cannot call .kill("SIGTERM", true)', testInvalidKillArgument, 'SIGTERM', true); -test('Cannot call .kill(true, error)', testInvalidKillArgument, true, new Error('test')); - -test('subprocess errors are handled before spawn', async t => { - const subprocess = execa('forever.js'); - const cause = new Error('test'); - subprocess.emit('error', cause); - subprocess.kill(); - const error = await t.throwsAsync(subprocess); - t.is(error.cause, cause); - t.is(error.exitCode, undefined); - t.is(error.signal, undefined); - t.false(error.isTerminated); -}); - -test('subprocess errors are handled after spawn', async t => { - const subprocess = execa('forever.js'); - await once(subprocess, 'spawn'); - const cause = new Error('test'); - subprocess.emit('error', cause); - subprocess.kill(); - const error = await t.throwsAsync(subprocess); - t.is(error.cause, cause); - t.is(error.exitCode, undefined); - t.is(error.signal, 'SIGTERM'); - t.true(error.isTerminated); -}); - -test('subprocess double errors are handled after spawn', async t => { - const abortController = new AbortController(); - const subprocess = execa('forever.js', {cancelSignal: abortController.signal}); - await once(subprocess, 'spawn'); - const cause = new Error('test'); - subprocess.emit('error', cause); - await setImmediate(); - abortController.abort(); - subprocess.emit('error', cause); - const error = await t.throwsAsync(subprocess); - t.is(error.cause, cause); - t.is(error.exitCode, undefined); - t.is(error.signal, 'SIGTERM'); - t.true(error.isTerminated); -}); - -test('subprocess errors use killSignal', async t => { - const subprocess = execa('forever.js', {killSignal: 'SIGINT'}); - await once(subprocess, 'spawn'); - const cause = new Error('test'); - subprocess.emit('error', cause); - subprocess.kill(); - const error = await t.throwsAsync(subprocess); - t.is(error.cause, cause); - t.true(error.isTerminated); - t.is(error.signal, 'SIGINT'); -}); diff --git a/test/terminate/signal.js b/test/terminate/signal.js deleted file mode 100644 index 96d5114b7a..0000000000 --- a/test/terminate/signal.js +++ /dev/null @@ -1,87 +0,0 @@ -import test from 'ava'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; - -setFixtureDirectory(); - -const VALIDATION_MESSAGES = { - string: 'this signal name does not exist', - integer: 'this signal integer does not exist', - other: 'it must be a string or an integer', - rename: 'please rename it to', - zero: '0 cannot be used', -}; - -const validateMessage = (t, message, type) => { - t.true(message.includes(VALIDATION_MESSAGES[type])); - - if (type !== 'rename' && type !== 'zero') { - t.true(message.includes('Available signal names: \'SIGABRT\', ')); - t.true(message.includes('Available signal numbers: 1, ')); - } -}; - -const testInvalidKillSignal = (t, killSignal, type, execaMethod) => { - const {message} = t.throws(() => { - execaMethod('empty.js', {killSignal}); - }); - t.true(message.includes('Invalid option `killSignal`')); - validateMessage(t, message, type); -}; - -test('Cannot use killSignal: "SIGOTHER"', testInvalidKillSignal, 'SIGOTHER', 'string', execa); -test('Cannot use killSignal: "Sigterm"', testInvalidKillSignal, 'Sigterm', 'rename', execa); -test('Cannot use killSignal: "sigterm"', testInvalidKillSignal, 'sigterm', 'rename', execa); -test('Cannot use killSignal: -1', testInvalidKillSignal, -1, 'integer', execa); -test('Cannot use killSignal: 200', testInvalidKillSignal, 200, 'integer', execa); -test('Cannot use killSignal: 1n', testInvalidKillSignal, 1n, 'other', execa); -test('Cannot use killSignal: 1.5', testInvalidKillSignal, 1.5, 'other', execa); -test('Cannot use killSignal: Infinity', testInvalidKillSignal, Number.POSITIVE_INFINITY, 'other', execa); -test('Cannot use killSignal: NaN', testInvalidKillSignal, Number.NaN, 'other', execa); -test('Cannot use killSignal: false', testInvalidKillSignal, false, 'other', execa); -test('Cannot use killSignal: null', testInvalidKillSignal, null, 'other', execa); -test('Cannot use killSignal: symbol', testInvalidKillSignal, Symbol('test'), 'other', execa); -test('Cannot use killSignal: {}', testInvalidKillSignal, {}, 'other', execa); -test('Cannot use killSignal: 0', testInvalidKillSignal, 0, 'zero', execa); -test('Cannot use killSignal: "SIGOTHER", sync', testInvalidKillSignal, 'SIGOTHER', 'string', execaSync); -test('Cannot use killSignal: "Sigterm", sync', testInvalidKillSignal, 'Sigterm', 'rename', execaSync); -test('Cannot use killSignal: "sigterm", sync', testInvalidKillSignal, 'sigterm', 'rename', execaSync); -test('Cannot use killSignal: -1, sync', testInvalidKillSignal, -1, 'integer', execaSync); -test('Cannot use killSignal: 200, sync', testInvalidKillSignal, 200, 'integer', execaSync); -test('Cannot use killSignal: 1.5, sync', testInvalidKillSignal, 1.5, 'other', execaSync); -test('Cannot use killSignal: Infinity, sync', testInvalidKillSignal, Number.POSITIVE_INFINITY, 'other', execaSync); -test('Cannot use killSignal: NaN, sync', testInvalidKillSignal, Number.NaN, 'other', execaSync); -test('Cannot use killSignal: null, sync', testInvalidKillSignal, null, 'other', execaSync); -test('Cannot use killSignal: symbol, sync', testInvalidKillSignal, Symbol('test'), 'other', execaSync); -test('Cannot use killSignal: {}, sync', testInvalidKillSignal, {}, 'other', execaSync); -test('Cannot use killSignal: 0, sync', testInvalidKillSignal, 0, 'zero', execaSync); - -const testInvalidSignalArgument = async (t, signal, type) => { - const subprocess = execa('empty.js'); - const {message} = t.throws(() => { - subprocess.kill(signal); - }); - - if (type === 'other') { - t.true(message.includes('must be an error instance or a signal name string/integer')); - } else { - t.true(message.includes('Invalid `subprocess.kill()`\'s argument')); - validateMessage(t, message, type); - } - - await subprocess; -}; - -test('Cannot use subprocess.kill("SIGOTHER")', testInvalidSignalArgument, 'SIGOTHER', 'string'); -test('Cannot use subprocess.kill("Sigterm")', testInvalidSignalArgument, 'Sigterm', 'rename'); -test('Cannot use subprocess.kill("sigterm")', testInvalidSignalArgument, 'sigterm', 'rename'); -test('Cannot use subprocess.kill(-1)', testInvalidSignalArgument, -1, 'integer'); -test('Cannot use subprocess.kill(200)', testInvalidSignalArgument, 200, 'integer'); -test('Cannot use subprocess.kill(1n)', testInvalidSignalArgument, 1n, 'other'); -test('Cannot use subprocess.kill(1.5)', testInvalidSignalArgument, 1.5, 'other'); -test('Cannot use subprocess.kill(Infinity)', testInvalidSignalArgument, Number.POSITIVE_INFINITY, 'other'); -test('Cannot use subprocess.kill(NaN)', testInvalidSignalArgument, Number.NaN, 'other'); -test('Cannot use subprocess.kill(false)', testInvalidSignalArgument, false, 'other'); -test('Cannot use subprocess.kill(null)', testInvalidSignalArgument, null, 'other'); -test('Cannot use subprocess.kill(symbol)', testInvalidSignalArgument, Symbol('test'), 'other'); -test('Cannot use subprocess.kill({})', testInvalidSignalArgument, {}, 'other'); diff --git a/test/terminate/timeout.js b/test/terminate/timeout.js deleted file mode 100644 index 655b5a9115..0000000000 --- a/test/terminate/timeout.js +++ /dev/null @@ -1,76 +0,0 @@ -import test from 'ava'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory, FIXTURES_DIRECTORY} from '../helpers/fixtures-directory.js'; - -setFixtureDirectory(); - -test('timeout kills the subprocess if it times out', async t => { - const {isTerminated, signal, timedOut, originalMessage, shortMessage, message} = await t.throwsAsync(execa('forever.js', {timeout: 1})); - t.true(isTerminated); - t.is(signal, 'SIGTERM'); - t.true(timedOut); - t.is(originalMessage, undefined); - t.is(shortMessage, 'Command timed out after 1 milliseconds: forever.js'); - t.is(message, shortMessage); -}); - -test('timeout kills the subprocess if it times out, in sync mode', async t => { - const {isTerminated, signal, timedOut, originalMessage, shortMessage, message} = await t.throws(() => { - execaSync('node', ['forever.js'], {timeout: 1, cwd: FIXTURES_DIRECTORY}); - }); - t.true(isTerminated); - t.is(signal, 'SIGTERM'); - t.true(timedOut); - t.is(originalMessage, 'spawnSync node ETIMEDOUT'); - t.is(shortMessage, `Command timed out after 1 milliseconds: node forever.js\n${originalMessage}`); - t.is(message, shortMessage); -}); - -test('timeout does not kill the subprocess if it does not time out', async t => { - const {timedOut} = await execa('delay.js', ['500'], {timeout: 1e8}); - t.false(timedOut); -}); - -test('timeout uses killSignal', async t => { - const {isTerminated, signal, timedOut} = await t.throwsAsync(execa('forever.js', {timeout: 1, killSignal: 'SIGINT'})); - t.true(isTerminated); - t.is(signal, 'SIGINT'); - t.true(timedOut); -}); - -const INVALID_TIMEOUT_REGEXP = /`timeout` option to be a non-negative integer/; - -const testTimeoutValidation = (t, timeout, execaMethod) => { - t.throws(() => { - execaMethod('empty.js', {timeout}); - }, {message: INVALID_TIMEOUT_REGEXP}); -}; - -test('timeout must not be negative', testTimeoutValidation, -1, execa); -test('timeout must be an integer', testTimeoutValidation, false, execa); -test('timeout must not be negative - sync', testTimeoutValidation, -1, execaSync); -test('timeout must be an integer - sync', testTimeoutValidation, false, execaSync); - -test('timedOut is false if timeout is undefined', async t => { - const {timedOut} = await execa('noop.js'); - t.false(timedOut); -}); - -test('timedOut is false if timeout is 0', async t => { - const {timedOut} = await execa('noop.js', {timeout: 0}); - t.false(timedOut); -}); - -test('timedOut is false if timeout is undefined and exit code is 0 in sync mode', t => { - const {timedOut} = execaSync('noop.js'); - t.false(timedOut); -}); - -test('timedOut is false if the timeout happened after a different error occurred', async t => { - const subprocess = execa('forever.js', {timeout: 1e3}); - const cause = new Error('test'); - subprocess.emit('error', cause); - const error = await t.throwsAsync(subprocess); - t.is(error.cause, cause); - t.false(error.timedOut); -}); diff --git a/test/transform/encoding-final.js b/test/transform/encoding-final.js deleted file mode 100644 index ff66b223aa..0000000000 --- a/test/transform/encoding-final.js +++ /dev/null @@ -1,129 +0,0 @@ -import {Buffer} from 'node:buffer'; -import {exec} from 'node:child_process'; -import {promisify} from 'node:util'; -import test from 'ava'; -import getStream from 'get-stream'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory, FIXTURES_DIRECTORY} from '../helpers/fixtures-directory.js'; -import {fullStdio} from '../helpers/stdio.js'; -import {foobarString, foobarUint8Array, foobarHex} from '../helpers/input.js'; - -const pExec = promisify(exec); - -setFixtureDirectory(); - -const checkEncoding = async (t, encoding, fdNumber, execaMethod) => { - const {stdio} = await execaMethod('noop-fd.js', [`${fdNumber}`, STRING_TO_ENCODE], {...fullStdio, encoding}); - compareValues(t, stdio[fdNumber], encoding); - - if (execaMethod !== execaSync) { - const subprocess = execaMethod('noop-fd.js', [`${fdNumber}`, STRING_TO_ENCODE], {...fullStdio, encoding}); - const result = await getStream(subprocess.stdio[fdNumber]); - compareValues(t, result, 'utf8'); - await subprocess; - } - - if (fdNumber === 3) { - return; - } - - const {stdout, stderr} = await pExec(`node noop-fd.js ${fdNumber} ${STRING_TO_ENCODE}`, {encoding, cwd: FIXTURES_DIRECTORY}); - compareValues(t, fdNumber === 1 ? stdout : stderr, encoding); -}; - -const compareValues = (t, value, encoding) => { - if (encoding === 'buffer') { - t.true(ArrayBuffer.isView(value)); - t.true(BUFFER_TO_ENCODE.equals(value)); - } else { - t.is(value, BUFFER_TO_ENCODE.toString(encoding)); - } -}; - -// This string gives different outputs with each encoding type -const STRING_TO_ENCODE = '\u1000.'; -const BUFFER_TO_ENCODE = Buffer.from(STRING_TO_ENCODE); - -test('can pass encoding "buffer" to stdout', checkEncoding, 'buffer', 1, execa); -test('can pass encoding "utf8" to stdout', checkEncoding, 'utf8', 1, execa); -test('can pass encoding "utf16le" to stdout', checkEncoding, 'utf16le', 1, execa); -test('can pass encoding "latin1" to stdout', checkEncoding, 'latin1', 1, execa); -test('can pass encoding "ascii" to stdout', checkEncoding, 'ascii', 1, execa); -test('can pass encoding "hex" to stdout', checkEncoding, 'hex', 1, execa); -test('can pass encoding "base64" to stdout', checkEncoding, 'base64', 1, execa); -test('can pass encoding "base64url" to stdout', checkEncoding, 'base64url', 1, execa); -test('can pass encoding "buffer" to stderr', checkEncoding, 'buffer', 2, execa); -test('can pass encoding "utf8" to stderr', checkEncoding, 'utf8', 2, execa); -test('can pass encoding "utf16le" to stderr', checkEncoding, 'utf16le', 2, execa); -test('can pass encoding "latin1" to stderr', checkEncoding, 'latin1', 2, execa); -test('can pass encoding "ascii" to stderr', checkEncoding, 'ascii', 2, execa); -test('can pass encoding "hex" to stderr', checkEncoding, 'hex', 2, execa); -test('can pass encoding "base64" to stderr', checkEncoding, 'base64', 2, execa); -test('can pass encoding "base64url" to stderr', checkEncoding, 'base64url', 2, execa); -test('can pass encoding "buffer" to stdio[*]', checkEncoding, 'buffer', 3, execa); -test('can pass encoding "utf8" to stdio[*]', checkEncoding, 'utf8', 3, execa); -test('can pass encoding "utf16le" to stdio[*]', checkEncoding, 'utf16le', 3, execa); -test('can pass encoding "latin1" to stdio[*]', checkEncoding, 'latin1', 3, execa); -test('can pass encoding "ascii" to stdio[*]', checkEncoding, 'ascii', 3, execa); -test('can pass encoding "hex" to stdio[*]', checkEncoding, 'hex', 3, execa); -test('can pass encoding "base64" to stdio[*]', checkEncoding, 'base64', 3, execa); -test('can pass encoding "base64url" to stdio[*]', checkEncoding, 'base64url', 3, execa); -test('can pass encoding "buffer" to stdout - sync', checkEncoding, 'buffer', 1, execaSync); -test('can pass encoding "utf8" to stdout - sync', checkEncoding, 'utf8', 1, execaSync); -test('can pass encoding "utf16le" to stdout - sync', checkEncoding, 'utf16le', 1, execaSync); -test('can pass encoding "latin1" to stdout - sync', checkEncoding, 'latin1', 1, execaSync); -test('can pass encoding "ascii" to stdout - sync', checkEncoding, 'ascii', 1, execaSync); -test('can pass encoding "hex" to stdout - sync', checkEncoding, 'hex', 1, execaSync); -test('can pass encoding "base64" to stdout - sync', checkEncoding, 'base64', 1, execaSync); -test('can pass encoding "base64url" to stdout - sync', checkEncoding, 'base64url', 1, execaSync); -test('can pass encoding "buffer" to stderr - sync', checkEncoding, 'buffer', 2, execaSync); -test('can pass encoding "utf8" to stderr - sync', checkEncoding, 'utf8', 2, execaSync); -test('can pass encoding "utf16le" to stderr - sync', checkEncoding, 'utf16le', 2, execaSync); -test('can pass encoding "latin1" to stderr - sync', checkEncoding, 'latin1', 2, execaSync); -test('can pass encoding "ascii" to stderr - sync', checkEncoding, 'ascii', 2, execaSync); -test('can pass encoding "hex" to stderr - sync', checkEncoding, 'hex', 2, execaSync); -test('can pass encoding "base64" to stderr - sync', checkEncoding, 'base64', 2, execaSync); -test('can pass encoding "base64url" to stderr - sync', checkEncoding, 'base64url', 2, execaSync); -test('can pass encoding "buffer" to stdio[*] - sync', checkEncoding, 'buffer', 3, execaSync); -test('can pass encoding "utf8" to stdio[*] - sync', checkEncoding, 'utf8', 3, execaSync); -test('can pass encoding "utf16le" to stdio[*] - sync', checkEncoding, 'utf16le', 3, execaSync); -test('can pass encoding "latin1" to stdio[*] - sync', checkEncoding, 'latin1', 3, execaSync); -test('can pass encoding "ascii" to stdio[*] - sync', checkEncoding, 'ascii', 3, execaSync); -test('can pass encoding "hex" to stdio[*] - sync', checkEncoding, 'hex', 3, execaSync); -test('can pass encoding "base64" to stdio[*] - sync', checkEncoding, 'base64', 3, execaSync); -test('can pass encoding "base64url" to stdio[*] - sync', checkEncoding, 'base64url', 3, execaSync); - -// eslint-disable-next-line max-params -const testEncodingInput = async (t, input, expectedStdout, encoding, execaMethod) => { - const {stdout} = await execaMethod('stdin.js', {input, encoding}); - t.deepEqual(stdout, expectedStdout); -}; - -test('Can use string input', testEncodingInput, foobarString, foobarString, 'utf8', execa); -test('Can use Uint8Array input', testEncodingInput, foobarUint8Array, foobarString, 'utf8', execa); -test('Can use string input, encoding "buffer"', testEncodingInput, foobarString, foobarUint8Array, 'buffer', execa); -test('Can use Uint8Array input, encoding "buffer"', testEncodingInput, foobarUint8Array, foobarUint8Array, 'buffer', execa); -test('Can use string input, encoding "hex"', testEncodingInput, foobarString, foobarHex, 'hex', execa); -test('Can use Uint8Array input, encoding "hex"', testEncodingInput, foobarUint8Array, foobarHex, 'hex', execa); -test('Can use string input, sync', testEncodingInput, foobarString, foobarString, 'utf8', execaSync); -test('Can use Uint8Array input, sync', testEncodingInput, foobarUint8Array, foobarString, 'utf8', execaSync); -test('Can use string input, encoding "buffer", sync', testEncodingInput, foobarString, foobarUint8Array, 'buffer', execaSync); -test('Can use Uint8Array input, encoding "buffer", sync', testEncodingInput, foobarUint8Array, foobarUint8Array, 'buffer', execaSync); -test('Can use string input, encoding "hex", sync', testEncodingInput, foobarString, foobarHex, 'hex', execaSync); -test('Can use Uint8Array input, encoding "hex", sync', testEncodingInput, foobarUint8Array, foobarHex, 'hex', execaSync); - -const testSubprocessEncoding = (t, encoding) => { - const subprocess = execa('empty.js', {...fullStdio, encoding}); - t.is(subprocess.stdout.readableEncoding, null); - t.is(subprocess.stderr.readableEncoding, null); - t.is(subprocess.stdio[3].readableEncoding, null); -}; - -test('Does not modify subprocess.std* encoding, "utf8"', testSubprocessEncoding, 'utf8'); -test('Does not modify subprocess.std* encoding, "utf16le"', testSubprocessEncoding, 'utf16le'); -test('Does not modify subprocess.std* encoding, "buffer"', testSubprocessEncoding, 'buffer'); -test('Does not modify subprocess.std* encoding, "hex"', testSubprocessEncoding, 'hex'); -test('Does not modify subprocess.std* encoding, "base64"', testSubprocessEncoding, 'base64'); -test('Does not modify subprocess.std* encoding, "base64url"', testSubprocessEncoding, 'base64url'); -test('Does not modify subprocess.std* encoding, "latin1"', testSubprocessEncoding, 'latin1'); -test('Does not modify subprocess.std* encoding, "ascii"', testSubprocessEncoding, 'ascii'); diff --git a/test/transform/encoding-ignored.js b/test/transform/encoding-ignored.js deleted file mode 100644 index 0a8ffe5cc1..0000000000 --- a/test/transform/encoding-ignored.js +++ /dev/null @@ -1,92 +0,0 @@ -import process from 'node:process'; -import test from 'ava'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {outputObjectGenerator, addNoopGenerator} from '../helpers/generator.js'; -import {foobarObject} from '../helpers/input.js'; - -setFixtureDirectory(); - -const testObjectMode = async (t, addNoopTransform, execaMethod) => { - const {stdout} = await execaMethod('noop.js', { - stdout: addNoopGenerator(outputObjectGenerator(), addNoopTransform, true), - encoding: 'base64', - }); - t.deepEqual(stdout, [foobarObject]); -}; - -test('Other encodings work with transforms that return objects', testObjectMode, false, execa); -test('Other encodings work with transforms that return objects, noop transform', testObjectMode, true, execa); -test('Other encodings work with transforms that return objects, sync', testObjectMode, false, execaSync); -test('Other encodings work with transforms that return objects, noop transform, sync', testObjectMode, true, execaSync); - -// eslint-disable-next-line max-params -const testIgnoredEncoding = async (t, stdoutOption, isUndefined, options, execaMethod) => { - const {stdout} = await execaMethod('empty.js', {stdout: stdoutOption, ...options}); - t.is(stdout === undefined, isUndefined); -}; - -const base64Options = {encoding: 'base64'}; -const linesOptions = {lines: true}; -test('Is ignored with other encodings and "ignore"', testIgnoredEncoding, 'ignore', true, base64Options, execa); -test('Is ignored with other encodings and ["ignore"]', testIgnoredEncoding, ['ignore'], true, base64Options, execa); -test('Is ignored with other encodings and "ipc"', testIgnoredEncoding, 'ipc', true, base64Options, execa); -test('Is ignored with other encodings and ["ipc"]', testIgnoredEncoding, ['ipc'], true, base64Options, execa); -test('Is ignored with other encodings and "inherit"', testIgnoredEncoding, 'inherit', true, base64Options, execa); -test('Is ignored with other encodings and ["inherit"]', testIgnoredEncoding, ['inherit'], true, base64Options, execa); -test('Is ignored with other encodings and 1', testIgnoredEncoding, 1, true, base64Options, execa); -test('Is ignored with other encodings and [1]', testIgnoredEncoding, [1], true, base64Options, execa); -test('Is ignored with other encodings and process.stdout', testIgnoredEncoding, process.stdout, true, base64Options, execa); -test('Is ignored with other encodings and [process.stdout]', testIgnoredEncoding, [process.stdout], true, base64Options, execa); -test('Is not ignored with other encodings and "pipe"', testIgnoredEncoding, 'pipe', false, base64Options, execa); -test('Is not ignored with other encodings and ["pipe"]', testIgnoredEncoding, ['pipe'], false, base64Options, execa); -test('Is not ignored with other encodings and "overlapped"', testIgnoredEncoding, 'overlapped', false, base64Options, execa); -test('Is not ignored with other encodings and ["overlapped"]', testIgnoredEncoding, ['overlapped'], false, base64Options, execa); -test('Is not ignored with other encodings and ["inherit", "pipe"]', testIgnoredEncoding, ['inherit', 'pipe'], false, base64Options, execa); -test('Is not ignored with other encodings and undefined', testIgnoredEncoding, undefined, false, base64Options, execa); -test('Is not ignored with other encodings and null', testIgnoredEncoding, null, false, base64Options, execa); -test('Is ignored with "lines: true" and "ignore"', testIgnoredEncoding, 'ignore', true, linesOptions, execa); -test('Is ignored with "lines: true" and ["ignore"]', testIgnoredEncoding, ['ignore'], true, linesOptions, execa); -test('Is ignored with "lines: true" and "ipc"', testIgnoredEncoding, 'ipc', true, linesOptions, execa); -test('Is ignored with "lines: true" and ["ipc"]', testIgnoredEncoding, ['ipc'], true, linesOptions, execa); -test('Is ignored with "lines: true" and "inherit"', testIgnoredEncoding, 'inherit', true, linesOptions, execa); -test('Is ignored with "lines: true" and ["inherit"]', testIgnoredEncoding, ['inherit'], true, linesOptions, execa); -test('Is ignored with "lines: true" and 1', testIgnoredEncoding, 1, true, linesOptions, execa); -test('Is ignored with "lines: true" and [1]', testIgnoredEncoding, [1], true, linesOptions, execa); -test('Is ignored with "lines: true" and process.stdout', testIgnoredEncoding, process.stdout, true, linesOptions, execa); -test('Is ignored with "lines: true" and [process.stdout]', testIgnoredEncoding, [process.stdout], true, linesOptions, execa); -test('Is not ignored with "lines: true" and "pipe"', testIgnoredEncoding, 'pipe', false, linesOptions, execa); -test('Is not ignored with "lines: true" and ["pipe"]', testIgnoredEncoding, ['pipe'], false, linesOptions, execa); -test('Is not ignored with "lines: true" and "overlapped"', testIgnoredEncoding, 'overlapped', false, linesOptions, execa); -test('Is not ignored with "lines: true" and ["overlapped"]', testIgnoredEncoding, ['overlapped'], false, linesOptions, execa); -test('Is not ignored with "lines: true" and ["inherit", "pipe"]', testIgnoredEncoding, ['inherit', 'pipe'], false, linesOptions, execa); -test('Is not ignored with "lines: true" and undefined', testIgnoredEncoding, undefined, false, linesOptions, execa); -test('Is not ignored with "lines: true" and null', testIgnoredEncoding, null, false, linesOptions, execa); -test('Is ignored with "lines: true", other encodings and "ignore"', testIgnoredEncoding, 'ignore', true, {...base64Options, ...linesOptions}, execa); -test('Is not ignored with "lines: true", other encodings and "pipe"', testIgnoredEncoding, 'pipe', false, {...base64Options, ...linesOptions}, execa); -test('Is ignored with other encodings and "ignore", sync', testIgnoredEncoding, 'ignore', true, base64Options, execaSync); -test('Is ignored with other encodings and ["ignore"], sync', testIgnoredEncoding, ['ignore'], true, base64Options, execaSync); -test('Is ignored with other encodings and "inherit", sync', testIgnoredEncoding, 'inherit', true, base64Options, execaSync); -test('Is ignored with other encodings and ["inherit"], sync', testIgnoredEncoding, ['inherit'], true, base64Options, execaSync); -test('Is ignored with other encodings and 1, sync', testIgnoredEncoding, 1, true, base64Options, execaSync); -test('Is ignored with other encodings and [1], sync', testIgnoredEncoding, [1], true, base64Options, execaSync); -test('Is ignored with other encodings and process.stdout, sync', testIgnoredEncoding, process.stdout, true, base64Options, execaSync); -test('Is ignored with other encodings and [process.stdout], sync', testIgnoredEncoding, [process.stdout], true, base64Options, execaSync); -test('Is not ignored with other encodings and "pipe", sync', testIgnoredEncoding, 'pipe', false, base64Options, execaSync); -test('Is not ignored with other encodings and ["pipe"], sync', testIgnoredEncoding, ['pipe'], false, base64Options, execaSync); -test('Is not ignored with other encodings and undefined, sync', testIgnoredEncoding, undefined, false, base64Options, execaSync); -test('Is not ignored with other encodings and null, sync', testIgnoredEncoding, null, false, base64Options, execaSync); -test('Is ignored with "lines: true" and "ignore", sync', testIgnoredEncoding, 'ignore', true, linesOptions, execaSync); -test('Is ignored with "lines: true" and ["ignore"], sync', testIgnoredEncoding, ['ignore'], true, linesOptions, execaSync); -test('Is ignored with "lines: true" and "inherit", sync', testIgnoredEncoding, 'inherit', true, linesOptions, execaSync); -test('Is ignored with "lines: true" and ["inherit"], sync', testIgnoredEncoding, ['inherit'], true, linesOptions, execaSync); -test('Is ignored with "lines: true" and 1, sync', testIgnoredEncoding, 1, true, linesOptions, execaSync); -test('Is ignored with "lines: true" and [1], sync', testIgnoredEncoding, [1], true, linesOptions, execaSync); -test('Is ignored with "lines: true" and process.stdout, sync', testIgnoredEncoding, process.stdout, true, linesOptions, execaSync); -test('Is ignored with "lines: true" and [process.stdout], sync', testIgnoredEncoding, [process.stdout], true, linesOptions, execaSync); -test('Is not ignored with "lines: true" and "pipe", sync', testIgnoredEncoding, 'pipe', false, linesOptions, execaSync); -test('Is not ignored with "lines: true" and ["pipe"], sync', testIgnoredEncoding, ['pipe'], false, linesOptions, execaSync); -test('Is not ignored with "lines: true" and undefined, sync', testIgnoredEncoding, undefined, false, linesOptions, execaSync); -test('Is not ignored with "lines: true" and null, sync', testIgnoredEncoding, null, false, linesOptions, execaSync); -test('Is ignored with "lines: true", other encodings and "ignore", sync', testIgnoredEncoding, 'ignore', true, {...base64Options, ...linesOptions}, execaSync); -test('Is not ignored with "lines: true", other encodings and "pipe", sync', testIgnoredEncoding, 'pipe', false, {...base64Options, ...linesOptions}, execaSync); diff --git a/test/transform/encoding-multibyte.js b/test/transform/encoding-multibyte.js deleted file mode 100644 index 62d2efa845..0000000000 --- a/test/transform/encoding-multibyte.js +++ /dev/null @@ -1,79 +0,0 @@ -import test from 'ava'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {noopGenerator, getOutputsGenerator, addNoopGenerator} from '../helpers/generator.js'; -import { - multibyteChar, - multibyteString, - multibyteUint8Array, - breakingLength, - brokenSymbol, -} from '../helpers/encoding.js'; - -setFixtureDirectory(); - -const foobarArray = ['fo', 'ob', 'ar', '..']; - -const testMultibyteCharacters = async (t, objectMode, addNoopTransform, execaMethod) => { - const {stdout} = await execaMethod('noop.js', { - stdout: addNoopGenerator(getOutputsGenerator(foobarArray)(objectMode, true), addNoopTransform, objectMode), - encoding: 'base64', - }); - if (objectMode) { - t.deepEqual(stdout, foobarArray); - } else { - t.is(stdout, Buffer.from(foobarArray.join('')).toString('base64')); - } -}; - -test('Handle multibyte characters', testMultibyteCharacters, false, false, execa); -test('Handle multibyte characters, noop transform', testMultibyteCharacters, false, true, execa); -test('Handle multibyte characters, with objectMode', testMultibyteCharacters, true, false, execa); -test('Handle multibyte characters, with objectMode, noop transform', testMultibyteCharacters, true, true, execa); -test('Handle multibyte characters, sync', testMultibyteCharacters, false, false, execaSync); -test('Handle multibyte characters, noop transform, sync', testMultibyteCharacters, false, true, execaSync); -test('Handle multibyte characters, with objectMode, sync', testMultibyteCharacters, true, false, execaSync); -test('Handle multibyte characters, with objectMode, noop transform, sync', testMultibyteCharacters, true, true, execaSync); - -const testMultibyte = async (t, objectMode, execaMethod) => { - const {stdout} = await execaMethod('stdin.js', { - stdin: [ - [multibyteUint8Array.slice(0, breakingLength), multibyteUint8Array.slice(breakingLength)], - noopGenerator(objectMode, true), - ], - }); - t.is(stdout, multibyteString); -}; - -test('Generator handles multibyte characters with Uint8Array', testMultibyte, false, execa); -test('Generator handles multibyte characters with Uint8Array, objectMode', testMultibyte, true, execa); -test('Generator handles multibyte characters with Uint8Array, sync', testMultibyte, false, execaSync); -test('Generator handles multibyte characters with Uint8Array, objectMode, sync', testMultibyte, true, execaSync); - -const testMultibytePartial = async (t, objectMode, execaMethod) => { - const {stdout} = await execaMethod('stdin.js', { - stdin: [ - [multibyteUint8Array.slice(0, breakingLength)], - noopGenerator(objectMode, true), - ], - }); - t.is(stdout, `${multibyteChar}${brokenSymbol}`); -}; - -test('Generator handles partial multibyte characters with Uint8Array', testMultibytePartial, false, execa); -test('Generator handles partial multibyte characters with Uint8Array, objectMode', testMultibytePartial, true, execa); -test('Generator handles partial multibyte characters with Uint8Array, sync', testMultibytePartial, false, execaSync); -test('Generator handles partial multibyte characters with Uint8Array, objectMode, sync', testMultibytePartial, true, execaSync); - -const testMultibytePartialOutput = async (t, execaMethod) => { - const {stdout} = await execaMethod('noop.js', { - stdout: getOutputsGenerator([ - multibyteUint8Array.slice(0, breakingLength), - multibyteUint8Array.slice(breakingLength), - ])(false, true), - }); - t.is(stdout, multibyteString); -}; - -test('Generator handles output multibyte characters with Uint8Array', testMultibytePartialOutput, execa); -test('Generator handles output multibyte characters with Uint8Array, sync', testMultibytePartialOutput, execaSync); diff --git a/test/transform/encoding-transform.js b/test/transform/encoding-transform.js deleted file mode 100644 index 54939b85f8..0000000000 --- a/test/transform/encoding-transform.js +++ /dev/null @@ -1,177 +0,0 @@ -import {Buffer} from 'node:buffer'; -import test from 'ava'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {getStdio} from '../helpers/stdio.js'; -import { - foobarString, - foobarUint8Array, - foobarBuffer, - foobarObject, -} from '../helpers/input.js'; -import {noopGenerator, getOutputGenerator} from '../helpers/generator.js'; - -setFixtureDirectory(); - -const getTypeofGenerator = lines => (objectMode, binary) => ({ - * transform(line) { - lines.push(Object.prototype.toString.call(line)); - yield ''; - }, - objectMode, - binary, -}); - -const assertTypeofChunk = (t, lines, expectedType) => { - t.deepEqual(lines, [`[object ${expectedType}]`]); -}; - -// eslint-disable-next-line max-params -const testGeneratorFirstEncoding = async (t, input, encoding, expectedType, objectMode, binary) => { - const lines = []; - const subprocess = execa('stdin.js', {stdin: getTypeofGenerator(lines)(objectMode, binary), encoding}); - subprocess.stdin.end(input); - await subprocess; - assertTypeofChunk(t, lines, expectedType); -}; - -test('First generator argument is string with default encoding, with string writes', testGeneratorFirstEncoding, foobarString, 'utf8', 'String', false, undefined); -test('First generator argument is string with default encoding, with Buffer writes', testGeneratorFirstEncoding, foobarBuffer, 'utf8', 'String', false, undefined); -test('First generator argument is string with default encoding, with Uint8Array writes', testGeneratorFirstEncoding, foobarUint8Array, 'utf8', 'String', false, undefined); -test('First generator argument is string with default encoding, with string writes, "binary: false"', testGeneratorFirstEncoding, foobarString, 'utf8', 'String', false, false); -test('First generator argument is Uint8Array with default encoding, with string writes, "binary: true"', testGeneratorFirstEncoding, foobarString, 'utf8', 'Uint8Array', false, true); -test('First generator argument is string with encoding "utf16le", with string writes', testGeneratorFirstEncoding, foobarString, 'utf16le', 'String', false, undefined); -test('First generator argument is string with encoding "utf16le", with Buffer writes', testGeneratorFirstEncoding, foobarBuffer, 'utf16le', 'String', false, undefined); -test('First generator argument is string with encoding "utf16le", with Uint8Array writes', testGeneratorFirstEncoding, foobarUint8Array, 'utf16le', 'String', false, undefined); -test('First generator argument is string with encoding "utf16le", with string writes, "binary: false"', testGeneratorFirstEncoding, foobarString, 'utf16le', 'String', false, false); -test('First generator argument is Uint8Array with encoding "utf16le", with string writes, "binary: true"', testGeneratorFirstEncoding, foobarString, 'utf16le', 'Uint8Array', false, true); -test('First generator argument is Uint8Array with encoding "buffer", with string writes', testGeneratorFirstEncoding, foobarString, 'buffer', 'Uint8Array', false, undefined); -test('First generator argument is Uint8Array with encoding "buffer", with Buffer writes', testGeneratorFirstEncoding, foobarBuffer, 'buffer', 'Uint8Array', false, undefined); -test('First generator argument is Uint8Array with encoding "buffer", with Uint8Array writes', testGeneratorFirstEncoding, foobarUint8Array, 'buffer', 'Uint8Array', false, undefined); -test('First generator argument is Uint8Array with encoding "buffer", with string writes, "binary: false"', testGeneratorFirstEncoding, foobarString, 'buffer', 'Uint8Array', false, false); -test('First generator argument is Uint8Array with encoding "buffer", with string writes, "binary: true"', testGeneratorFirstEncoding, foobarString, 'buffer', 'Uint8Array', false, true); -test('First generator argument is Uint8Array with encoding "hex", with string writes', testGeneratorFirstEncoding, foobarString, 'hex', 'Uint8Array', false, undefined); -test('First generator argument is Uint8Array with encoding "hex", with Buffer writes', testGeneratorFirstEncoding, foobarBuffer, 'hex', 'Uint8Array', false, undefined); -test('First generator argument is Uint8Array with encoding "hex", with Uint8Array writes', testGeneratorFirstEncoding, foobarUint8Array, 'hex', 'Uint8Array', false, undefined); -test('First generator argument is Uint8Array with encoding "hex", with string writes, "binary: false"', testGeneratorFirstEncoding, foobarString, 'hex', 'Uint8Array', false, false); -test('First generator argument is Uint8Array with encoding "hex", with string writes, "binary: true"', testGeneratorFirstEncoding, foobarString, 'hex', 'Uint8Array', false, true); -test('First generator argument can be string with objectMode', testGeneratorFirstEncoding, foobarString, 'utf8', 'String', true, undefined); -test('First generator argument can be string with objectMode, "binary: false"', testGeneratorFirstEncoding, foobarString, 'utf8', 'String', true, false); -test('First generator argument can be string with objectMode, "binary: true"', testGeneratorFirstEncoding, foobarString, 'utf8', 'String', true, true); -test('First generator argument can be objects with objectMode', testGeneratorFirstEncoding, foobarObject, 'utf8', 'Object', true, undefined); -test('First generator argument can be objects with objectMode, "binary: false"', testGeneratorFirstEncoding, foobarObject, 'utf8', 'Object', true, false); -test('First generator argument can be objects with objectMode, "binary: true"', testGeneratorFirstEncoding, foobarObject, 'utf8', 'Object', true, true); - -// eslint-disable-next-line max-params -const testGeneratorFirstEncodingSync = (t, input, encoding, expectedType, objectMode, binary) => { - const lines = []; - execaSync('stdin.js', {stdin: [[input], getTypeofGenerator(lines)(objectMode, binary)], encoding}); - assertTypeofChunk(t, lines, expectedType); -}; - -test('First generator argument is string with default encoding, with string writes, sync', testGeneratorFirstEncodingSync, foobarString, 'utf8', 'String', false, undefined); -test('First generator argument is string with default encoding, with Uint8Array writes, sync', testGeneratorFirstEncodingSync, foobarUint8Array, 'utf8', 'String', false, undefined); -test('First generator argument is string with default encoding, with string writes, "binary: false", sync', testGeneratorFirstEncodingSync, foobarString, 'utf8', 'String', false, false); -test('First generator argument is Uint8Array with default encoding, with string writes, "binary: true", sync', testGeneratorFirstEncodingSync, foobarString, 'utf8', 'Uint8Array', false, true); -test('First generator argument is string with encoding "utf16le", with string writes, sync', testGeneratorFirstEncodingSync, foobarString, 'utf16le', 'String', false, undefined); -test('First generator argument is string with encoding "utf16le", with Uint8Array writes, sync', testGeneratorFirstEncodingSync, foobarUint8Array, 'utf16le', 'String', false, undefined); -test('First generator argument is string with encoding "utf16le", with string writes, "binary: false", sync', testGeneratorFirstEncodingSync, foobarString, 'utf16le', 'String', false, false); -test('First generator argument is Uint8Array with encoding "utf16le", with string writes, "binary: true", sync', testGeneratorFirstEncodingSync, foobarString, 'utf16le', 'Uint8Array', false, true); -test('First generator argument is Uint8Array with encoding "buffer", with string writes, sync', testGeneratorFirstEncodingSync, foobarString, 'buffer', 'Uint8Array', false, undefined); -test('First generator argument is Uint8Array with encoding "buffer", with Uint8Array writes, sync', testGeneratorFirstEncodingSync, foobarUint8Array, 'buffer', 'Uint8Array', false, undefined); -test('First generator argument is Uint8Array with encoding "buffer", with string writes, "binary: false", sync', testGeneratorFirstEncodingSync, foobarString, 'buffer', 'Uint8Array', false, false); -test('First generator argument is Uint8Array with encoding "buffer", with string writes, "binary: true", sync', testGeneratorFirstEncodingSync, foobarString, 'buffer', 'Uint8Array', false, true); -test('First generator argument is Uint8Array with encoding "hex", with string writes, sync', testGeneratorFirstEncodingSync, foobarString, 'hex', 'Uint8Array', false, undefined); -test('First generator argument is Uint8Array with encoding "hex", with Uint8Array writes, sync', testGeneratorFirstEncodingSync, foobarUint8Array, 'hex', 'Uint8Array', false, undefined); -test('First generator argument is Uint8Array with encoding "hex", with string writes, "binary: false", sync', testGeneratorFirstEncodingSync, foobarString, 'hex', 'Uint8Array', false, false); -test('First generator argument is Uint8Array with encoding "hex", with string writes, "binary: true", sync', testGeneratorFirstEncodingSync, foobarString, 'hex', 'Uint8Array', false, true); -test('First generator argument can be string with objectMode, sync', testGeneratorFirstEncodingSync, foobarString, 'utf8', 'String', true, undefined); -test('First generator argument can be string with objectMode, "binary: false", sync', testGeneratorFirstEncodingSync, foobarString, 'utf8', 'String', true, false); -test('First generator argument can be string with objectMode, "binary: true", sync', testGeneratorFirstEncodingSync, foobarString, 'utf8', 'String', true, true); -test('First generator argument can be objects with objectMode, sync', testGeneratorFirstEncodingSync, foobarObject, 'utf8', 'Object', true, undefined); -test('First generator argument can be objects with objectMode, "binary: false", sync', testGeneratorFirstEncodingSync, foobarObject, 'utf8', 'Object', true, false); -test('First generator argument can be objects with objectMode, "binary: true", sync', testGeneratorFirstEncodingSync, foobarObject, 'utf8', 'Object', true, true); - -const testEncodingIgnored = async (t, encoding) => { - const input = Buffer.from(foobarString).toString(encoding); - const subprocess = execa('stdin.js', {stdin: noopGenerator(true)}); - subprocess.stdin.end(input, encoding); - const {stdout} = await subprocess; - t.is(stdout, input); -}; - -test('Write call encoding "utf8" is ignored with objectMode', testEncodingIgnored, 'utf8'); -test('Write call encoding "utf16le" is ignored with objectMode', testEncodingIgnored, 'utf16le'); -test('Write call encoding "hex" is ignored with objectMode', testEncodingIgnored, 'hex'); -test('Write call encoding "base64" is ignored with objectMode', testEncodingIgnored, 'base64'); - -// eslint-disable-next-line max-params -const testGeneratorNextEncoding = async (t, input, encoding, firstObjectMode, secondObjectMode, expectedType, execaMethod) => { - const lines = []; - await execaMethod('noop.js', ['other'], { - stdout: [ - getOutputGenerator(input)(firstObjectMode), - getTypeofGenerator(lines)(secondObjectMode), - ], - encoding, - }); - assertTypeofChunk(t, lines, expectedType); -}; - -test('Next generator argument is string with default encoding, with string writes', testGeneratorNextEncoding, foobarString, 'utf8', false, false, 'String', execa); -test('Next generator argument is string with default encoding, with string writes, objectMode first', testGeneratorNextEncoding, foobarString, 'utf8', true, false, 'String', execa); -test('Next generator argument is string with default encoding, with string writes, objectMode both', testGeneratorNextEncoding, foobarString, 'utf8', true, true, 'String', execa); -test('Next generator argument is string with default encoding, with Uint8Array writes', testGeneratorNextEncoding, foobarUint8Array, 'utf8', false, false, 'String', execa); -test('Next generator argument is Uint8Array with default encoding, with Uint8Array writes, objectMode first', testGeneratorNextEncoding, foobarUint8Array, 'utf8', true, false, 'Uint8Array', execa); -test('Next generator argument is string with default encoding, with Uint8Array writes, objectMode both', testGeneratorNextEncoding, foobarUint8Array, 'utf8', true, true, 'Uint8Array', execa); -test('Next generator argument is string with encoding "utf16le", with string writes', testGeneratorNextEncoding, foobarString, 'utf16le', false, false, 'String', execa); -test('Next generator argument is string with encoding "utf16le",, with string writes, objectMode first', testGeneratorNextEncoding, foobarString, 'utf16le', true, false, 'String', execa); -test('Next generator argument is string with encoding "utf16le",, with string writes, objectMode both', testGeneratorNextEncoding, foobarString, 'utf16le', true, true, 'String', execa); -test('Next generator argument is string with encoding "utf16le",, with Uint8Array writes', testGeneratorNextEncoding, foobarUint8Array, 'utf16le', false, false, 'String', execa); -test('Next generator argument is Uint8Array with encoding "utf16le",, with Uint8Array writes, objectMode first', testGeneratorNextEncoding, foobarUint8Array, 'utf16le', true, false, 'Uint8Array', execa); -test('Next generator argument is string with encoding "utf16le",, with Uint8Array writes, objectMode both', testGeneratorNextEncoding, foobarUint8Array, 'utf16le', true, true, 'Uint8Array', execa); -test('Next generator argument is Uint8Array with encoding "buffer", with string writes', testGeneratorNextEncoding, foobarString, 'buffer', false, false, 'Uint8Array', execa); -test('Next generator argument is string with encoding "buffer", with string writes, objectMode first', testGeneratorNextEncoding, foobarString, 'buffer', true, false, 'String', execa); -test('Next generator argument is string with encoding "buffer", with string writes, objectMode both', testGeneratorNextEncoding, foobarString, 'buffer', true, true, 'String', execa); -test('Next generator argument is Uint8Array with encoding "buffer", with Uint8Array writes', testGeneratorNextEncoding, foobarUint8Array, 'buffer', false, false, 'Uint8Array', execa); -test('Next generator argument is Uint8Array with encoding "buffer", with Uint8Array writes, objectMode first', testGeneratorNextEncoding, foobarUint8Array, 'buffer', true, false, 'Uint8Array', execa); -test('Next generator argument is Uint8Array with encoding "buffer", with Uint8Array writes, objectMode both', testGeneratorNextEncoding, foobarUint8Array, 'buffer', true, true, 'Uint8Array', execa); -test('Next generator argument is Uint8Array with encoding "hex", with string writes', testGeneratorNextEncoding, foobarString, 'hex', false, false, 'Uint8Array', execa); -test('Next generator argument is Uint8Array with encoding "hex", with Uint8Array writes', testGeneratorNextEncoding, foobarUint8Array, 'hex', false, false, 'Uint8Array', execa); -test('Next generator argument is object with default encoding, with object writes, objectMode first', testGeneratorNextEncoding, foobarObject, 'utf8', true, false, 'Object', execa); -test('Next generator argument is object with default encoding, with object writes, objectMode both', testGeneratorNextEncoding, foobarObject, 'utf8', true, true, 'Object', execa); -test('Next generator argument is string with default encoding, with string writes, sync', testGeneratorNextEncoding, foobarString, 'utf8', false, false, 'String', execaSync); -test('Next generator argument is string with default encoding, with string writes, objectMode first, sync', testGeneratorNextEncoding, foobarString, 'utf8', true, false, 'String', execaSync); -test('Next generator argument is string with default encoding, with string writes, objectMode both, sync', testGeneratorNextEncoding, foobarString, 'utf8', true, true, 'String', execaSync); -test('Next generator argument is string with default encoding, with Uint8Array writes, sync', testGeneratorNextEncoding, foobarUint8Array, 'utf8', false, false, 'String', execaSync); -test('Next generator argument is Uint8Array with default encoding, with Uint8Array writes, objectMode first, sync', testGeneratorNextEncoding, foobarUint8Array, 'utf8', true, false, 'Uint8Array', execaSync); -test('Next generator argument is string with default encoding, with Uint8Array writes, objectMode both, sync', testGeneratorNextEncoding, foobarUint8Array, 'utf8', true, true, 'Uint8Array', execaSync); -test('Next generator argument is string with encoding "utf16le", with string writes, sync', testGeneratorNextEncoding, foobarString, 'utf16le', false, false, 'String', execaSync); -test('Next generator argument is string with encoding "utf16le",, with string writes, objectMode first, sync', testGeneratorNextEncoding, foobarString, 'utf16le', true, false, 'String', execaSync); -test('Next generator argument is string with encoding "utf16le",, with string writes, objectMode both, sync', testGeneratorNextEncoding, foobarString, 'utf16le', true, true, 'String', execaSync); -test('Next generator argument is string with encoding "utf16le",, with Uint8Array writes, sync', testGeneratorNextEncoding, foobarUint8Array, 'utf16le', false, false, 'String', execaSync); -test('Next generator argument is Uint8Array with encoding "utf16le",, with Uint8Array writes, objectMode first, sync', testGeneratorNextEncoding, foobarUint8Array, 'utf16le', true, false, 'Uint8Array', execaSync); -test('Next generator argument is string with encoding "utf16le",, with Uint8Array writes, objectMode both, sync', testGeneratorNextEncoding, foobarUint8Array, 'utf16le', true, true, 'Uint8Array', execaSync); -test('Next generator argument is Uint8Array with encoding "buffer", with string writes, sync', testGeneratorNextEncoding, foobarString, 'buffer', false, false, 'Uint8Array', execaSync); -test('Next generator argument is string with encoding "buffer", with string writes, objectMode first, sync', testGeneratorNextEncoding, foobarString, 'buffer', true, false, 'String', execaSync); -test('Next generator argument is string with encoding "buffer", with string writes, objectMode both, sync', testGeneratorNextEncoding, foobarString, 'buffer', true, true, 'String', execaSync); -test('Next generator argument is Uint8Array with encoding "buffer", with Uint8Array writes, sync', testGeneratorNextEncoding, foobarUint8Array, 'buffer', false, false, 'Uint8Array', execaSync); -test('Next generator argument is Uint8Array with encoding "buffer", with Uint8Array writes, objectMode first, sync', testGeneratorNextEncoding, foobarUint8Array, 'buffer', true, false, 'Uint8Array', execaSync); -test('Next generator argument is Uint8Array with encoding "buffer", with Uint8Array writes, objectMode both, sync', testGeneratorNextEncoding, foobarUint8Array, 'buffer', true, true, 'Uint8Array', execaSync); -test('Next generator argument is Uint8Array with encoding "hex", with string writes, sync', testGeneratorNextEncoding, foobarString, 'hex', false, false, 'Uint8Array', execaSync); -test('Next generator argument is Uint8Array with encoding "hex", with Uint8Array writes, sync', testGeneratorNextEncoding, foobarUint8Array, 'hex', false, false, 'Uint8Array', execaSync); -test('Next generator argument is object with default encoding, with object writes, objectMode first, sync', testGeneratorNextEncoding, foobarObject, 'utf8', true, false, 'Object', execaSync); -test('Next generator argument is object with default encoding, with object writes, objectMode both, sync', testGeneratorNextEncoding, foobarObject, 'utf8', true, true, 'Object', execaSync); - -const testFirstOutputGeneratorArgument = async (t, fdNumber, execaMethod) => { - const lines = []; - await execaMethod('noop-fd.js', [`${fdNumber}`], getStdio(fdNumber, getTypeofGenerator(lines)(true))); - assertTypeofChunk(t, lines, 'String'); -}; - -test('The first generator with result.stdout does not receive an object argument even in objectMode', testFirstOutputGeneratorArgument, 1, execa); -test('The first generator with result.stderr does not receive an object argument even in objectMode', testFirstOutputGeneratorArgument, 2, execa); -test('The first generator with result.stdio[*] does not receive an object argument even in objectMode', testFirstOutputGeneratorArgument, 3, execa); -test('The first generator with result.stdout does not receive an object argument even in objectMode, sync', testFirstOutputGeneratorArgument, 1, execaSync); -test('The first generator with result.stderr does not receive an object argument even in objectMode, sync', testFirstOutputGeneratorArgument, 2, execaSync); -test('The first generator with result.stdio[*] does not receive an object argument even in objectMode, sync', testFirstOutputGeneratorArgument, 3, execaSync); diff --git a/test/transform/generator-all.js b/test/transform/generator-all.js deleted file mode 100644 index 0312b189b2..0000000000 --- a/test/transform/generator-all.js +++ /dev/null @@ -1,242 +0,0 @@ -import {Buffer} from 'node:buffer'; -import test from 'ava'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarObject} from '../helpers/input.js'; -import { - outputObjectGenerator, - uppercaseGenerator, - uppercaseBufferGenerator, -} from '../helpers/generator.js'; - -setFixtureDirectory(); - -const textEncoder = new TextEncoder(); - -const getAllStdioOption = (stdioOption, encoding, objectMode) => { - if (stdioOption) { - return 'pipe'; - } - - if (objectMode) { - return outputObjectGenerator(); - } - - return encoding === 'utf8' ? uppercaseGenerator() : uppercaseBufferGenerator(); -}; - -const getStdoutStderrOutput = ({output, stdioOption, encoding, objectMode, lines}) => { - if (objectMode && !stdioOption) { - return encoding === 'utf8' ? [foobarObject, foobarObject] : [foobarObject]; - } - - const stdioOutput = stdioOption ? output : output.toUpperCase(); - - if (encoding === 'hex') { - return Buffer.from(stdioOutput).toString('hex'); - } - - if (encoding === 'buffer') { - return textEncoder.encode(stdioOutput); - } - - return lines ? stdioOutput.trim().split('\n').map(string => `${string}\n`) : stdioOutput; -}; - -const getAllOutput = ({stdoutOutput, stderrOutput, encoding, objectMode, lines}) => { - if (objectMode || (lines && encoding === 'utf8')) { - return [stdoutOutput, stderrOutput].flat(); - } - - return encoding === 'buffer' - ? new Uint8Array([...stdoutOutput, ...stderrOutput]) - : `${stdoutOutput}${stderrOutput}`; -}; - -// eslint-disable-next-line max-params -const testGeneratorAll = async (t, reject, encoding, objectMode, stdoutOption, stderrOption, lines, execaMethod) => { - const fixtureName = reject ? 'all.js' : 'all-fail.js'; - const {stdout, stderr, all} = await execaMethod(fixtureName, { - all: true, - reject, - stdout: getAllStdioOption(stdoutOption, encoding, objectMode), - stderr: getAllStdioOption(stderrOption, encoding, objectMode), - encoding, - lines, - stripFinalNewline: false, - }); - - const stdoutOutput = getStdoutStderrOutput({ - output: 'std\nout\n', - stdioOption: stdoutOption, - encoding, - objectMode, - lines, - }); - t.deepEqual(stdout, stdoutOutput); - const stderrOutput = getStdoutStderrOutput({ - output: 'std\nerr\n', - stdioOption: stderrOption, - encoding, - objectMode, - lines, - }); - t.deepEqual(stderr, stderrOutput); - const allOutput = getAllOutput({ - stdoutOutput, - stderrOutput, - encoding, - objectMode, - lines, - }); - if (Array.isArray(all) && Array.isArray(allOutput)) { - t.deepEqual([...all].sort(), [...allOutput].sort()); - } else { - t.deepEqual(all, allOutput); - } -}; - -test('Can use generators with result.all = transform + transform', testGeneratorAll, true, 'utf8', false, false, false, false, execa); -test('Can use generators with error.all = transform + transform', testGeneratorAll, false, 'utf8', false, false, false, false, execa); -test('Can use generators with result.all = transform + transform, encoding "buffer"', testGeneratorAll, true, 'buffer', false, false, false, false, execa); -test('Can use generators with error.all = transform + transform, encoding "buffer"', testGeneratorAll, false, 'buffer', false, false, false, false, execa); -test('Can use generators with result.all = transform + transform, encoding "hex"', testGeneratorAll, true, 'hex', false, false, false, false, execa); -test('Can use generators with error.all = transform + transform, encoding "hex"', testGeneratorAll, false, 'hex', false, false, false, false, execa); -test('Can use generators with result.all = transform + pipe', testGeneratorAll, true, 'utf8', false, false, true, false, execa); -test('Can use generators with error.all = transform + pipe', testGeneratorAll, false, 'utf8', false, false, true, false, execa); -test('Can use generators with result.all = transform + pipe, encoding "buffer"', testGeneratorAll, true, 'buffer', false, false, true, false, execa); -test('Can use generators with error.all = transform + pipe, encoding "buffer"', testGeneratorAll, false, 'buffer', false, false, true, false, execa); -test('Can use generators with result.all = transform + pipe, encoding "hex"', testGeneratorAll, true, 'hex', false, false, true, false, execa); -test('Can use generators with error.all = transform + pipe, encoding "hex"', testGeneratorAll, false, 'hex', false, false, true, false, execa); -test('Can use generators with result.all = pipe + transform', testGeneratorAll, true, 'utf8', false, true, false, false, execa); -test('Can use generators with error.all = pipe + transform', testGeneratorAll, false, 'utf8', false, true, false, false, execa); -test('Can use generators with result.all = pipe + transform, encoding "buffer"', testGeneratorAll, true, 'buffer', false, true, false, false, execa); -test('Can use generators with error.all = pipe + transform, encoding "buffer"', testGeneratorAll, false, 'buffer', false, true, false, false, execa); -test('Can use generators with result.all = pipe + transform, encoding "hex"', testGeneratorAll, true, 'hex', false, true, false, false, execa); -test('Can use generators with error.all = pipe + transform, encoding "hex"', testGeneratorAll, false, 'hex', false, true, false, false, execa); -test('Can use generators with result.all = transform + transform, objectMode', testGeneratorAll, true, 'utf8', true, false, false, false, execa); -test('Can use generators with error.all = transform + transform, objectMode', testGeneratorAll, false, 'utf8', true, false, false, false, execa); -test('Can use generators with result.all = transform + transform, objectMode, encoding "buffer"', testGeneratorAll, true, 'buffer', true, false, false, false, execa); -test('Can use generators with error.all = transform + transform, objectMode, encoding "buffer"', testGeneratorAll, false, 'buffer', true, false, false, false, execa); -test('Can use generators with result.all = transform + transform, objectMode, encoding "hex"', testGeneratorAll, true, 'hex', true, false, false, false, execa); -test('Can use generators with error.all = transform + transform, objectMode, encoding "hex"', testGeneratorAll, false, 'hex', true, false, false, false, execa); -test('Can use generators with result.all = transform + pipe, objectMode', testGeneratorAll, true, 'utf8', true, false, true, false, execa); -test('Can use generators with error.all = transform + pipe, objectMode', testGeneratorAll, false, 'utf8', true, false, true, false, execa); -test('Can use generators with result.all = transform + pipe, objectMode, encoding "buffer"', testGeneratorAll, true, 'buffer', true, false, true, false, execa); -test('Can use generators with error.all = transform + pipe, objectMode, encoding "buffer"', testGeneratorAll, false, 'buffer', true, false, true, false, execa); -test('Can use generators with result.all = transform + pipe, objectMode, encoding "hex"', testGeneratorAll, true, 'hex', true, false, true, false, execa); -test('Can use generators with error.all = transform + pipe, objectMode, encoding "hex"', testGeneratorAll, false, 'hex', true, false, true, false, execa); -test('Can use generators with result.all = pipe + transform, objectMode', testGeneratorAll, true, 'utf8', true, true, false, false, execa); -test('Can use generators with error.all = pipe + transform, objectMode', testGeneratorAll, false, 'utf8', true, true, false, false, execa); -test('Can use generators with result.all = pipe + transform, objectMode, encoding "buffer"', testGeneratorAll, true, 'buffer', true, true, false, false, execa); -test('Can use generators with error.all = pipe + transform, objectMode, encoding "buffer"', testGeneratorAll, false, 'buffer', true, true, false, false, execa); -test('Can use generators with result.all = pipe + transform, objectMode, encoding "hex"', testGeneratorAll, true, 'hex', true, true, false, false, execa); -test('Can use generators with error.all = pipe + transform, objectMode, encoding "hex"', testGeneratorAll, false, 'hex', true, true, false, false, execa); -test('Can use generators with result.all = transform + transform, sync', testGeneratorAll, true, 'utf8', false, false, false, false, execaSync); -test('Can use generators with error.all = transform + transform, sync', testGeneratorAll, false, 'utf8', false, false, false, false, execaSync); -test('Can use generators with result.all = transform + transform, encoding "buffer", sync', testGeneratorAll, true, 'buffer', false, false, false, false, execaSync); -test('Can use generators with error.all = transform + transform, encoding "buffer", sync', testGeneratorAll, false, 'buffer', false, false, false, false, execaSync); -test('Can use generators with result.all = transform + transform, encoding "hex", sync', testGeneratorAll, true, 'hex', false, false, false, false, execaSync); -test('Can use generators with error.all = transform + transform, encoding "hex", sync', testGeneratorAll, false, 'hex', false, false, false, false, execaSync); -test('Can use generators with result.all = transform + pipe, sync', testGeneratorAll, true, 'utf8', false, false, true, false, execaSync); -test('Can use generators with error.all = transform + pipe, sync', testGeneratorAll, false, 'utf8', false, false, true, false, execaSync); -test('Can use generators with result.all = transform + pipe, encoding "buffer", sync', testGeneratorAll, true, 'buffer', false, false, true, false, execaSync); -test('Can use generators with error.all = transform + pipe, encoding "buffer", sync', testGeneratorAll, false, 'buffer', false, false, true, false, execaSync); -test('Can use generators with result.all = transform + pipe, encoding "hex", sync', testGeneratorAll, true, 'hex', false, false, true, false, execaSync); -test('Can use generators with error.all = transform + pipe, encoding "hex", sync', testGeneratorAll, false, 'hex', false, false, true, false, execaSync); -test('Can use generators with result.all = pipe + transform, sync', testGeneratorAll, true, 'utf8', false, true, false, false, execaSync); -test('Can use generators with error.all = pipe + transform, sync', testGeneratorAll, false, 'utf8', false, true, false, false, execaSync); -test('Can use generators with result.all = pipe + transform, encoding "buffer", sync', testGeneratorAll, true, 'buffer', false, true, false, false, execaSync); -test('Can use generators with error.all = pipe + transform, encoding "buffer", sync', testGeneratorAll, false, 'buffer', false, true, false, false, execaSync); -test('Can use generators with result.all = pipe + transform, encoding "hex", sync', testGeneratorAll, true, 'hex', false, true, false, false, execaSync); -test('Can use generators with error.all = pipe + transform, encoding "hex", sync', testGeneratorAll, false, 'hex', false, true, false, false, execaSync); -test('Can use generators with result.all = transform + transform, objectMode, sync', testGeneratorAll, true, 'utf8', true, false, false, false, execaSync); -test('Can use generators with error.all = transform + transform, objectMode, sync', testGeneratorAll, false, 'utf8', true, false, false, false, execaSync); -test('Can use generators with result.all = transform + transform, objectMode, encoding "buffer", sync', testGeneratorAll, true, 'buffer', true, false, false, false, execaSync); -test('Can use generators with error.all = transform + transform, objectMode, encoding "buffer", sync', testGeneratorAll, false, 'buffer', true, false, false, false, execaSync); -test('Can use generators with result.all = transform + transform, objectMode, encoding "hex", sync', testGeneratorAll, true, 'hex', true, false, false, false, execaSync); -test('Can use generators with error.all = transform + transform, objectMode, encoding "hex", sync', testGeneratorAll, false, 'hex', true, false, false, false, execaSync); -test('Can use generators with result.all = transform + pipe, objectMode, sync', testGeneratorAll, true, 'utf8', true, false, true, false, execaSync); -test('Can use generators with error.all = transform + pipe, objectMode, sync', testGeneratorAll, false, 'utf8', true, false, true, false, execaSync); -test('Can use generators with result.all = transform + pipe, objectMode, encoding "buffer", sync', testGeneratorAll, true, 'buffer', true, false, true, false, execaSync); -test('Can use generators with error.all = transform + pipe, objectMode, encoding "buffer", sync', testGeneratorAll, false, 'buffer', true, false, true, false, execaSync); -test('Can use generators with result.all = transform + pipe, objectMode, encoding "hex", sync', testGeneratorAll, true, 'hex', true, false, true, false, execaSync); -test('Can use generators with error.all = transform + pipe, objectMode, encoding "hex", sync', testGeneratorAll, false, 'hex', true, false, true, false, execaSync); -test('Can use generators with result.all = pipe + transform, objectMode, sync', testGeneratorAll, true, 'utf8', true, true, false, false, execaSync); -test('Can use generators with error.all = pipe + transform, objectMode, sync', testGeneratorAll, false, 'utf8', true, true, false, false, execaSync); -test('Can use generators with result.all = pipe + transform, objectMode, encoding "buffer", sync', testGeneratorAll, true, 'buffer', true, true, false, false, execaSync); -test('Can use generators with error.all = pipe + transform, objectMode, encoding "buffer", sync', testGeneratorAll, false, 'buffer', true, true, false, false, execaSync); -test('Can use generators with result.all = pipe + transform, objectMode, encoding "hex", sync', testGeneratorAll, true, 'hex', true, true, false, false, execaSync); -test('Can use generators with error.all = pipe + transform, objectMode, encoding "hex", sync', testGeneratorAll, false, 'hex', true, true, false, false, execaSync); -test('Can use generators with result.all = transform + transform, lines', testGeneratorAll, true, 'utf8', false, false, false, true, execa); -test('Can use generators with error.all = transform + transform, lines', testGeneratorAll, false, 'utf8', false, false, false, true, execa); -test('Can use generators with result.all = transform + transform, encoding "buffer", lines', testGeneratorAll, true, 'buffer', false, false, false, true, execa); -test('Can use generators with error.all = transform + transform, encoding "buffer", lines', testGeneratorAll, false, 'buffer', false, false, false, true, execa); -test('Can use generators with result.all = transform + transform, encoding "hex", lines', testGeneratorAll, true, 'hex', false, false, false, true, execa); -test('Can use generators with error.all = transform + transform, encoding "hex", lines', testGeneratorAll, false, 'hex', false, false, false, true, execa); -test('Can use generators with result.all = transform + pipe, lines', testGeneratorAll, true, 'utf8', false, false, true, true, execa); -test('Can use generators with error.all = transform + pipe, lines', testGeneratorAll, false, 'utf8', false, false, true, true, execa); -test('Can use generators with result.all = transform + pipe, encoding "buffer", lines', testGeneratorAll, true, 'buffer', false, false, true, true, execa); -test('Can use generators with error.all = transform + pipe, encoding "buffer", lines', testGeneratorAll, false, 'buffer', false, false, true, true, execa); -test('Can use generators with result.all = transform + pipe, encoding "hex", lines', testGeneratorAll, true, 'hex', false, false, true, true, execa); -test('Can use generators with error.all = transform + pipe, encoding "hex", lines', testGeneratorAll, false, 'hex', false, false, true, true, execa); -test('Can use generators with result.all = pipe + transform, lines', testGeneratorAll, true, 'utf8', false, true, false, true, execa); -test('Can use generators with error.all = pipe + transform, lines', testGeneratorAll, false, 'utf8', false, true, false, true, execa); -test('Can use generators with result.all = pipe + transform, encoding "buffer", lines', testGeneratorAll, true, 'buffer', false, true, false, true, execa); -test('Can use generators with error.all = pipe + transform, encoding "buffer", lines', testGeneratorAll, false, 'buffer', false, true, false, true, execa); -test('Can use generators with result.all = pipe + transform, encoding "hex", lines', testGeneratorAll, true, 'hex', false, true, false, true, execa); -test('Can use generators with error.all = pipe + transform, encoding "hex", lines', testGeneratorAll, false, 'hex', false, true, false, true, execa); -test('Can use generators with result.all = transform + transform, objectMode, lines', testGeneratorAll, true, 'utf8', true, false, false, true, execa); -test('Can use generators with error.all = transform + transform, objectMode, lines', testGeneratorAll, false, 'utf8', true, false, false, true, execa); -test('Can use generators with result.all = transform + transform, objectMode, encoding "buffer", lines', testGeneratorAll, true, 'buffer', true, false, false, true, execa); -test('Can use generators with error.all = transform + transform, objectMode, encoding "buffer", lines', testGeneratorAll, false, 'buffer', true, false, false, true, execa); -test('Can use generators with result.all = transform + transform, objectMode, encoding "hex", lines', testGeneratorAll, true, 'hex', true, false, false, true, execa); -test('Can use generators with error.all = transform + transform, objectMode, encoding "hex", lines', testGeneratorAll, false, 'hex', true, false, false, true, execa); -test('Can use generators with result.all = transform + pipe, objectMode, lines', testGeneratorAll, true, 'utf8', true, false, true, true, execa); -test('Can use generators with error.all = transform + pipe, objectMode, lines', testGeneratorAll, false, 'utf8', true, false, true, true, execa); -test('Can use generators with result.all = transform + pipe, objectMode, encoding "buffer", lines', testGeneratorAll, true, 'buffer', true, false, true, true, execa); -test('Can use generators with error.all = transform + pipe, objectMode, encoding "buffer", lines', testGeneratorAll, false, 'buffer', true, false, true, true, execa); -test('Can use generators with result.all = transform + pipe, objectMode, encoding "hex", lines', testGeneratorAll, true, 'hex', true, false, true, true, execa); -test('Can use generators with error.all = transform + pipe, objectMode, encoding "hex", lines', testGeneratorAll, false, 'hex', true, false, true, true, execa); -test('Can use generators with result.all = pipe + transform, objectMode, lines', testGeneratorAll, true, 'utf8', true, true, false, true, execa); -test('Can use generators with error.all = pipe + transform, objectMode, lines', testGeneratorAll, false, 'utf8', true, true, false, true, execa); -test('Can use generators with result.all = pipe + transform, objectMode, encoding "buffer", lines', testGeneratorAll, true, 'buffer', true, true, false, true, execa); -test('Can use generators with error.all = pipe + transform, objectMode, encoding "buffer", lines', testGeneratorAll, false, 'buffer', true, true, false, true, execa); -test('Can use generators with result.all = pipe + transform, objectMode, encoding "hex", lines', testGeneratorAll, true, 'hex', true, true, false, true, execa); -test('Can use generators with error.all = pipe + transform, objectMode, encoding "hex", lines', testGeneratorAll, false, 'hex', true, true, false, true, execa); -test('Can use generators with result.all = transform + transform, sync, lines', testGeneratorAll, true, 'utf8', false, false, false, true, execaSync); -test('Can use generators with error.all = transform + transform, sync, lines', testGeneratorAll, false, 'utf8', false, false, false, true, execaSync); -test('Can use generators with result.all = transform + transform, encoding "buffer", sync, lines', testGeneratorAll, true, 'buffer', false, false, false, true, execaSync); -test('Can use generators with error.all = transform + transform, encoding "buffer", sync, lines', testGeneratorAll, false, 'buffer', false, false, false, true, execaSync); -test('Can use generators with result.all = transform + transform, encoding "hex", sync, lines', testGeneratorAll, true, 'hex', false, false, false, true, execaSync); -test('Can use generators with error.all = transform + transform, encoding "hex", sync, lines', testGeneratorAll, false, 'hex', false, false, false, true, execaSync); -test('Can use generators with result.all = transform + pipe, sync, lines', testGeneratorAll, true, 'utf8', false, false, true, true, execaSync); -test('Can use generators with error.all = transform + pipe, sync, lines', testGeneratorAll, false, 'utf8', false, false, true, true, execaSync); -test('Can use generators with result.all = transform + pipe, encoding "buffer", sync, lines', testGeneratorAll, true, 'buffer', false, false, true, true, execaSync); -test('Can use generators with error.all = transform + pipe, encoding "buffer", sync, lines', testGeneratorAll, false, 'buffer', false, false, true, true, execaSync); -test('Can use generators with result.all = transform + pipe, encoding "hex", sync, lines', testGeneratorAll, true, 'hex', false, false, true, true, execaSync); -test('Can use generators with error.all = transform + pipe, encoding "hex", sync, lines', testGeneratorAll, false, 'hex', false, false, true, true, execaSync); -test('Can use generators with result.all = pipe + transform, sync, lines', testGeneratorAll, true, 'utf8', false, true, false, true, execaSync); -test('Can use generators with error.all = pipe + transform, sync, lines', testGeneratorAll, false, 'utf8', false, true, false, true, execaSync); -test('Can use generators with result.all = pipe + transform, encoding "buffer", sync, lines', testGeneratorAll, true, 'buffer', false, true, false, true, execaSync); -test('Can use generators with error.all = pipe + transform, encoding "buffer", sync, lines', testGeneratorAll, false, 'buffer', false, true, false, true, execaSync); -test('Can use generators with result.all = pipe + transform, encoding "hex", sync, lines', testGeneratorAll, true, 'hex', false, true, false, true, execaSync); -test('Can use generators with error.all = pipe + transform, encoding "hex", sync, lines', testGeneratorAll, false, 'hex', false, true, false, true, execaSync); -test('Can use generators with result.all = transform + transform, objectMode, sync, lines', testGeneratorAll, true, 'utf8', true, false, false, true, execaSync); -test('Can use generators with error.all = transform + transform, objectMode, sync, lines', testGeneratorAll, false, 'utf8', true, false, false, true, execaSync); -test('Can use generators with result.all = transform + transform, objectMode, encoding "buffer", sync, lines', testGeneratorAll, true, 'buffer', true, false, false, true, execaSync); -test('Can use generators with error.all = transform + transform, objectMode, encoding "buffer", sync, lines', testGeneratorAll, false, 'buffer', true, false, false, true, execaSync); -test('Can use generators with result.all = transform + transform, objectMode, encoding "hex", sync, lines', testGeneratorAll, true, 'hex', true, false, false, true, execaSync); -test('Can use generators with error.all = transform + transform, objectMode, encoding "hex", sync, lines', testGeneratorAll, false, 'hex', true, false, false, true, execaSync); -test('Can use generators with result.all = transform + pipe, objectMode, sync, lines', testGeneratorAll, true, 'utf8', true, false, true, true, execaSync); -test('Can use generators with error.all = transform + pipe, objectMode, sync, lines', testGeneratorAll, false, 'utf8', true, false, true, true, execaSync); -test('Can use generators with result.all = transform + pipe, objectMode, encoding "buffer", sync, lines', testGeneratorAll, true, 'buffer', true, false, true, true, execaSync); -test('Can use generators with error.all = transform + pipe, objectMode, encoding "buffer", sync, lines', testGeneratorAll, false, 'buffer', true, false, true, true, execaSync); -test('Can use generators with result.all = transform + pipe, objectMode, encoding "hex", sync, lines', testGeneratorAll, true, 'hex', true, false, true, true, execaSync); -test('Can use generators with error.all = transform + pipe, objectMode, encoding "hex", sync, lines', testGeneratorAll, false, 'hex', true, false, true, true, execaSync); -test('Can use generators with result.all = pipe + transform, objectMode, sync, lines', testGeneratorAll, true, 'utf8', true, true, false, true, execaSync); -test('Can use generators with error.all = pipe + transform, objectMode, sync, lines', testGeneratorAll, false, 'utf8', true, true, false, true, execaSync); -test('Can use generators with result.all = pipe + transform, objectMode, encoding "buffer", sync, lines', testGeneratorAll, true, 'buffer', true, true, false, true, execaSync); -test('Can use generators with error.all = pipe + transform, objectMode, encoding "buffer", sync, lines', testGeneratorAll, false, 'buffer', true, true, false, true, execaSync); -test('Can use generators with result.all = pipe + transform, objectMode, encoding "hex", sync, lines', testGeneratorAll, true, 'hex', true, true, false, true, execaSync); -test('Can use generators with error.all = pipe + transform, objectMode, encoding "hex", sync, lines', testGeneratorAll, false, 'hex', true, true, false, true, execaSync); diff --git a/test/transform/generator-error.js b/test/transform/generator-error.js deleted file mode 100644 index 691b08cee6..0000000000 --- a/test/transform/generator-error.js +++ /dev/null @@ -1,91 +0,0 @@ -import {once} from 'node:events'; -import test from 'ava'; -import {execa, execaSync} from '../../index.js'; -import {foobarString} from '../helpers/input.js'; -import {noopGenerator, infiniteGenerator, convertTransformToFinal} from '../helpers/generator.js'; -import {generatorsMap} from '../helpers/map.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {getEarlyErrorSubprocess, expectedEarlyError} from '../helpers/early-error.js'; - -setFixtureDirectory(); - -const assertProcessError = async (t, type, execaMethod, getSubprocess) => { - const cause = new Error(foobarString); - const transform = generatorsMap[type].throwing(cause)(); - const error = execaMethod === execa - ? await t.throwsAsync(getSubprocess(transform)) - : t.throws(() => { - getSubprocess(transform); - }); - t.is(error.cause, cause); -}; - -const testThrowingGenerator = async (t, type, final, execaMethod) => { - await assertProcessError(t, type, execaMethod, transform => execaMethod('noop.js', { - stdout: convertTransformToFinal(transform, final), - })); -}; - -test('Generators "transform" errors make subprocess fail', testThrowingGenerator, 'generator', false, execa); -test('Generators "final" errors make subprocess fail', testThrowingGenerator, 'generator', true, execa); -test('Generators "transform" errors make subprocess fail, sync', testThrowingGenerator, 'generator', false, execaSync); -test('Generators "final" errors make subprocess fail, sync', testThrowingGenerator, 'generator', true, execaSync); -test('Duplexes "transform" errors make subprocess fail', testThrowingGenerator, 'duplex', false, execa); -test('WebTransform "transform" errors make subprocess fail', testThrowingGenerator, 'webTransform', false, execa); - -const testSingleErrorOutput = async (t, type, execaMethod) => { - await assertProcessError(t, type, execaMethod, transform => execaMethod('noop.js', { - stdout: [ - generatorsMap[type].noop(false), - transform, - generatorsMap[type].noop(false), - ], - })); -}; - -test('Generators errors make subprocess fail even when other output generators do not throw', testSingleErrorOutput, 'generator', execa); -test('Generators errors make subprocess fail even when other output generators do not throw, sync', testSingleErrorOutput, 'generator', execaSync); -test('Duplexes errors make subprocess fail even when other output generators do not throw', testSingleErrorOutput, 'duplex', execa); -test('WebTransform errors make subprocess fail even when other output generators do not throw', testSingleErrorOutput, 'webTransform', execa); - -const testSingleErrorInput = async (t, type, execaMethod) => { - await assertProcessError(t, type, execaMethod, transform => execaMethod('stdin.js', { - stdin: [ - ['foobar\n'], - generatorsMap[type].noop(false), - transform, - generatorsMap[type].noop(false), - ], - })); -}; - -test('Generators errors make subprocess fail even when other input generators do not throw', testSingleErrorInput, 'generator', execa); -test('Generators errors make subprocess fail even when other input generators do not throw, sync', testSingleErrorInput, 'generator', execaSync); -test('Duplexes errors make subprocess fail even when other input generators do not throw', testSingleErrorInput, 'duplex', execa); -test('WebTransform errors make subprocess fail even when other input generators do not throw', testSingleErrorInput, 'webTransform', execa); - -const testGeneratorCancel = async (t, error) => { - const subprocess = execa('noop.js', {stdout: infiniteGenerator()}); - await once(subprocess.stdout, 'data'); - subprocess.stdout.destroy(error); - await (error === undefined ? t.notThrowsAsync(subprocess) : t.throwsAsync(subprocess)); -}; - -test('Running generators are canceled on subprocess abort', testGeneratorCancel, undefined); -test('Running generators are canceled on subprocess error', testGeneratorCancel, new Error('test')); - -const testGeneratorDestroy = async (t, transform) => { - const subprocess = execa('forever.js', {stdout: transform}); - const cause = new Error('test'); - subprocess.stdout.destroy(cause); - subprocess.kill(); - t.like(await t.throwsAsync(subprocess), {cause}); -}; - -test('Generators are destroyed on subprocess error, sync', testGeneratorDestroy, noopGenerator(false)); -test('Generators are destroyed on subprocess error, async', testGeneratorDestroy, infiniteGenerator()); - -test('Generators are destroyed on early subprocess exit', async t => { - const error = await t.throwsAsync(getEarlyErrorSubprocess({stdout: infiniteGenerator()})); - t.like(error, expectedEarlyError); -}); diff --git a/test/transform/generator-final.js b/test/transform/generator-final.js deleted file mode 100644 index 3eb3cdcac7..0000000000 --- a/test/transform/generator-final.js +++ /dev/null @@ -1,35 +0,0 @@ -import test from 'ava'; -import {execa, execaSync} from '../../index.js'; -import {foobarString} from '../helpers/input.js'; -import {getOutputAsyncGenerator, getOutputGenerator, convertTransformToFinal} from '../helpers/generator.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; - -setFixtureDirectory(); - -const testGeneratorFinal = async (t, fixtureName, execaMethod) => { - const {stdout} = await execaMethod(fixtureName, {stdout: convertTransformToFinal(getOutputGenerator(foobarString)(), true)}); - t.is(stdout, foobarString); -}; - -test('Generators "final" can be used', testGeneratorFinal, 'noop.js', execa); -test('Generators "final" is used even on empty streams', testGeneratorFinal, 'empty.js', execa); -test('Generators "final" can be used, sync', testGeneratorFinal, 'noop.js', execaSync); -test('Generators "final" is used even on empty streams, sync', testGeneratorFinal, 'empty.js', execaSync); - -const testFinalAlone = async (t, final, execaMethod) => { - const {stdout} = await execaMethod('noop-fd.js', ['1', '.'], {stdout: {final: final(foobarString)().transform}}); - t.is(stdout, `.\n${foobarString}`); -}; - -test('Generators "final" can be used without "transform"', testFinalAlone, getOutputGenerator, execa); -test('Generators "final" can be used without "transform", sync', testFinalAlone, getOutputGenerator, execaSync); -test('Generators "final" can be used without "transform", async', testFinalAlone, getOutputAsyncGenerator, execa); - -const testFinalNoOutput = async (t, final, execaMethod) => { - const {stdout} = await execaMethod('empty.js', {stdout: {final: final(foobarString)().transform}}); - t.is(stdout, foobarString); -}; - -test('Generators "final" can be used without "transform" nor output', testFinalNoOutput, getOutputGenerator, execa); -test('Generators "final" can be used without "transform" nor output, sync', testFinalNoOutput, getOutputGenerator, execaSync); -test('Generators "final" can be used without "transform" nor output, async', testFinalNoOutput, getOutputAsyncGenerator, execa); diff --git a/test/transform/generator-input.js b/test/transform/generator-input.js deleted file mode 100644 index 212cc3d955..0000000000 --- a/test/transform/generator-input.js +++ /dev/null @@ -1,158 +0,0 @@ -import {Buffer} from 'node:buffer'; -import test from 'ava'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {getStdio} from '../helpers/stdio.js'; -import { - foobarString, - foobarUppercase, - foobarHex, - foobarUint8Array, - foobarBuffer, - foobarObject, - foobarObjectString, -} from '../helpers/input.js'; -import {generatorsMap} from '../helpers/map.js'; - -setFixtureDirectory(); - -const getInputObjectMode = (objectMode, addNoopTransform, type) => objectMode - ? { - input: [foobarObject], - generators: generatorsMap[type].addNoop(generatorsMap[type].serialize(objectMode), addNoopTransform, objectMode), - output: foobarObjectString, - } - : { - input: foobarUint8Array, - generators: generatorsMap[type].addNoop(generatorsMap[type].uppercase(objectMode), addNoopTransform, objectMode), - output: foobarUppercase, - }; - -// eslint-disable-next-line max-params -const testGeneratorInput = async (t, fdNumber, objectMode, addNoopTransform, type, execaMethod) => { - const {input, generators, output} = getInputObjectMode(objectMode, addNoopTransform, type); - const {stdout} = await execaMethod('stdin-fd.js', [`${fdNumber}`], getStdio(fdNumber, [input, ...generators])); - t.is(stdout, output); -}; - -test('Can use generators with result.stdin', testGeneratorInput, 0, false, false, 'generator', execa); -test('Can use generators with result.stdio[*] as input', testGeneratorInput, 3, false, false, 'generator', execa); -test('Can use generators with result.stdin, objectMode', testGeneratorInput, 0, true, false, 'generator', execa); -test('Can use generators with result.stdio[*] as input, objectMode', testGeneratorInput, 3, true, false, 'generator', execa); -test('Can use generators with result.stdin, noop transform', testGeneratorInput, 0, false, true, 'generator', execa); -test('Can use generators with result.stdio[*] as input, noop transform', testGeneratorInput, 3, false, true, 'generator', execa); -test('Can use generators with result.stdin, objectMode, noop transform', testGeneratorInput, 0, true, true, 'generator', execa); -test('Can use generators with result.stdio[*] as input, objectMode, noop transform', testGeneratorInput, 3, true, true, 'generator', execa); -test('Can use generators with result.stdin, sync', testGeneratorInput, 0, false, false, 'generator', execaSync); -test('Can use generators with result.stdin, objectMode, sync', testGeneratorInput, 0, true, false, 'generator', execaSync); -test('Can use generators with result.stdin, noop transform, sync', testGeneratorInput, 0, false, true, 'generator', execaSync); -test('Can use generators with result.stdin, objectMode, noop transform, sync', testGeneratorInput, 0, true, true, 'generator', execaSync); -test('Can use duplexes with result.stdin', testGeneratorInput, 0, false, false, 'duplex', execa); -test('Can use duplexes with result.stdio[*] as input', testGeneratorInput, 3, false, false, 'duplex', execa); -test('Can use duplexes with result.stdin, objectMode', testGeneratorInput, 0, true, false, 'duplex', execa); -test('Can use duplexes with result.stdio[*] as input, objectMode', testGeneratorInput, 3, true, false, 'duplex', execa); -test('Can use duplexes with result.stdin, noop transform', testGeneratorInput, 0, false, true, 'duplex', execa); -test('Can use duplexes with result.stdio[*] as input, noop transform', testGeneratorInput, 3, false, true, 'duplex', execa); -test('Can use duplexes with result.stdin, objectMode, noop transform', testGeneratorInput, 0, true, true, 'duplex', execa); -test('Can use duplexes with result.stdio[*] as input, objectMode, noop transform', testGeneratorInput, 3, true, true, 'duplex', execa); -test('Can use webTransforms with result.stdin', testGeneratorInput, 0, false, false, 'webTransform', execa); -test('Can use webTransforms with result.stdio[*] as input', testGeneratorInput, 3, false, false, 'webTransform', execa); -test('Can use webTransforms with result.stdin, objectMode', testGeneratorInput, 0, true, false, 'webTransform', execa); -test('Can use webTransforms with result.stdio[*] as input, objectMode', testGeneratorInput, 3, true, false, 'webTransform', execa); -test('Can use webTransforms with result.stdin, noop transform', testGeneratorInput, 0, false, true, 'webTransform', execa); -test('Can use webTransforms with result.stdio[*] as input, noop transform', testGeneratorInput, 3, false, true, 'webTransform', execa); -test('Can use webTransforms with result.stdin, objectMode, noop transform', testGeneratorInput, 0, true, true, 'webTransform', execa); -test('Can use webTransforms with result.stdio[*] as input, objectMode, noop transform', testGeneratorInput, 3, true, true, 'webTransform', execa); - -// eslint-disable-next-line max-params -const testGeneratorInputPipe = async (t, useShortcutProperty, objectMode, addNoopTransform, type, input) => { - const {generators, output} = getInputObjectMode(objectMode, addNoopTransform, type); - const subprocess = execa('stdin-fd.js', ['0'], getStdio(0, generators)); - const stream = useShortcutProperty ? subprocess.stdin : subprocess.stdio[0]; - stream.end(...input); - const {stdout} = await subprocess; - const expectedOutput = input[1] === 'utf16le' ? Buffer.from(output, input[1]).toString() : output; - t.is(stdout, expectedOutput); -}; - -test('Can use generators with subprocess.stdio[0] and default encoding', testGeneratorInputPipe, false, false, false, 'generator', [foobarString, 'utf8']); -test('Can use generators with subprocess.stdin and default encoding', testGeneratorInputPipe, true, false, false, 'generator', [foobarString, 'utf8']); -test('Can use generators with subprocess.stdio[0] and encoding "utf16le"', testGeneratorInputPipe, false, false, false, 'generator', [foobarString, 'utf16le']); -test('Can use generators with subprocess.stdin and encoding "utf16le"', testGeneratorInputPipe, true, false, false, 'generator', [foobarString, 'utf16le']); -test('Can use generators with subprocess.stdio[0] and encoding "buffer"', testGeneratorInputPipe, false, false, false, 'generator', [foobarBuffer, 'buffer']); -test('Can use generators with subprocess.stdin and encoding "buffer"', testGeneratorInputPipe, true, false, false, 'generator', [foobarBuffer, 'buffer']); -test('Can use generators with subprocess.stdio[0] and encoding "hex"', testGeneratorInputPipe, false, false, false, 'generator', [foobarHex, 'hex']); -test('Can use generators with subprocess.stdin and encoding "hex"', testGeneratorInputPipe, true, false, false, 'generator', [foobarHex, 'hex']); -test('Can use generators with subprocess.stdio[0], objectMode', testGeneratorInputPipe, false, true, false, 'generator', [foobarObject]); -test('Can use generators with subprocess.stdin, objectMode', testGeneratorInputPipe, true, true, false, 'generator', [foobarObject]); -test('Can use generators with subprocess.stdio[0] and default encoding, noop transform', testGeneratorInputPipe, false, false, true, 'generator', [foobarString, 'utf8']); -test('Can use generators with subprocess.stdin and default encoding, noop transform', testGeneratorInputPipe, true, false, true, 'generator', [foobarString, 'utf8']); -test('Can use generators with subprocess.stdio[0] and encoding "utf16le", noop transform', testGeneratorInputPipe, false, false, true, 'generator', [foobarString, 'utf16le']); -test('Can use generators with subprocess.stdin and encoding "utf16le", noop transform', testGeneratorInputPipe, true, false, true, 'generator', [foobarString, 'utf16le']); -test('Can use generators with subprocess.stdio[0] and encoding "buffer", noop transform', testGeneratorInputPipe, false, false, true, 'generator', [foobarBuffer, 'buffer']); -test('Can use generators with subprocess.stdin and encoding "buffer", noop transform', testGeneratorInputPipe, true, false, true, 'generator', [foobarBuffer, 'buffer']); -test('Can use generators with subprocess.stdio[0] and encoding "hex", noop transform', testGeneratorInputPipe, false, false, true, 'generator', [foobarHex, 'hex']); -test('Can use generators with subprocess.stdin and encoding "hex", noop transform', testGeneratorInputPipe, true, false, true, 'generator', [foobarHex, 'hex']); -test('Can use generators with subprocess.stdio[0], objectMode, noop transform', testGeneratorInputPipe, false, true, true, 'generator', [foobarObject]); -test('Can use generators with subprocess.stdin, objectMode, noop transform', testGeneratorInputPipe, true, true, true, 'generator', [foobarObject]); -test('Can use duplexes with subprocess.stdio[0] and default encoding', testGeneratorInputPipe, false, false, false, 'duplex', [foobarString, 'utf8']); -test('Can use duplexes with subprocess.stdin and default encoding', testGeneratorInputPipe, true, false, false, 'duplex', [foobarString, 'utf8']); -test('Can use duplexes with subprocess.stdio[0] and encoding "utf16le"', testGeneratorInputPipe, false, false, false, 'duplex', [foobarString, 'utf16le']); -test('Can use duplexes with subprocess.stdin and encoding "utf16le"', testGeneratorInputPipe, true, false, false, 'duplex', [foobarString, 'utf16le']); -test('Can use duplexes with subprocess.stdio[0] and encoding "buffer"', testGeneratorInputPipe, false, false, false, 'duplex', [foobarBuffer, 'buffer']); -test('Can use duplexes with subprocess.stdin and encoding "buffer"', testGeneratorInputPipe, true, false, false, 'duplex', [foobarBuffer, 'buffer']); -test('Can use duplexes with subprocess.stdio[0] and encoding "hex"', testGeneratorInputPipe, false, false, false, 'duplex', [foobarHex, 'hex']); -test('Can use duplexes with subprocess.stdin and encoding "hex"', testGeneratorInputPipe, true, false, false, 'duplex', [foobarHex, 'hex']); -test('Can use duplexes with subprocess.stdio[0], objectMode', testGeneratorInputPipe, false, true, false, 'duplex', [foobarObject]); -test('Can use duplexes with subprocess.stdin, objectMode', testGeneratorInputPipe, true, true, false, 'duplex', [foobarObject]); -test('Can use duplexes with subprocess.stdio[0] and default encoding, noop transform', testGeneratorInputPipe, false, false, true, 'duplex', [foobarString, 'utf8']); -test('Can use duplexes with subprocess.stdin and default encoding, noop transform', testGeneratorInputPipe, true, false, true, 'duplex', [foobarString, 'utf8']); -test('Can use duplexes with subprocess.stdio[0] and encoding "utf16le", noop transform', testGeneratorInputPipe, false, false, true, 'duplex', [foobarString, 'utf16le']); -test('Can use duplexes with subprocess.stdin and encoding "utf16le", noop transform', testGeneratorInputPipe, true, false, true, 'duplex', [foobarString, 'utf16le']); -test('Can use duplexes with subprocess.stdio[0] and encoding "buffer", noop transform', testGeneratorInputPipe, false, false, true, 'duplex', [foobarBuffer, 'buffer']); -test('Can use duplexes with subprocess.stdin and encoding "buffer", noop transform', testGeneratorInputPipe, true, false, true, 'duplex', [foobarBuffer, 'buffer']); -test('Can use duplexes with subprocess.stdio[0] and encoding "hex", noop transform', testGeneratorInputPipe, false, false, true, 'duplex', [foobarHex, 'hex']); -test('Can use duplexes with subprocess.stdin and encoding "hex", noop transform', testGeneratorInputPipe, true, false, true, 'duplex', [foobarHex, 'hex']); -test('Can use duplexes with subprocess.stdio[0], objectMode, noop transform', testGeneratorInputPipe, false, true, true, 'duplex', [foobarObject]); -test('Can use duplexes with subprocess.stdin, objectMode, noop transform', testGeneratorInputPipe, true, true, true, 'duplex', [foobarObject]); -test('Can use webTransforms with subprocess.stdio[0] and default encoding', testGeneratorInputPipe, false, false, false, 'webTransform', [foobarString, 'utf8']); -test('Can use webTransforms with subprocess.stdin and default encoding', testGeneratorInputPipe, true, false, false, 'webTransform', [foobarString, 'utf8']); -test('Can use webTransforms with subprocess.stdio[0] and encoding "utf16le"', testGeneratorInputPipe, false, false, false, 'webTransform', [foobarString, 'utf16le']); -test('Can use webTransforms with subprocess.stdin and encoding "utf16le"', testGeneratorInputPipe, true, false, false, 'webTransform', [foobarString, 'utf16le']); -test('Can use webTransforms with subprocess.stdio[0] and encoding "buffer"', testGeneratorInputPipe, false, false, false, 'webTransform', [foobarBuffer, 'buffer']); -test('Can use webTransforms with subprocess.stdin and encoding "buffer"', testGeneratorInputPipe, true, false, false, 'webTransform', [foobarBuffer, 'buffer']); -test('Can use webTransforms with subprocess.stdio[0] and encoding "hex"', testGeneratorInputPipe, false, false, false, 'webTransform', [foobarHex, 'hex']); -test('Can use webTransforms with subprocess.stdin and encoding "hex"', testGeneratorInputPipe, true, false, false, 'webTransform', [foobarHex, 'hex']); -test('Can use webTransforms with subprocess.stdio[0], objectMode', testGeneratorInputPipe, false, true, false, 'webTransform', [foobarObject]); -test('Can use webTransforms with subprocess.stdin, objectMode', testGeneratorInputPipe, true, true, false, 'webTransform', [foobarObject]); -test('Can use webTransforms with subprocess.stdio[0] and default encoding, noop transform', testGeneratorInputPipe, false, false, true, 'webTransform', [foobarString, 'utf8']); -test('Can use webTransforms with subprocess.stdin and default encoding, noop transform', testGeneratorInputPipe, true, false, true, 'webTransform', [foobarString, 'utf8']); -test('Can use webTransforms with subprocess.stdio[0] and encoding "utf16le", noop transform', testGeneratorInputPipe, false, false, true, 'webTransform', [foobarString, 'utf16le']); -test('Can use webTransforms with subprocess.stdin and encoding "utf16le", noop transform', testGeneratorInputPipe, true, false, true, 'webTransform', [foobarString, 'utf16le']); -test('Can use webTransforms with subprocess.stdio[0] and encoding "buffer", noop transform', testGeneratorInputPipe, false, false, true, 'webTransform', [foobarBuffer, 'buffer']); -test('Can use webTransforms with subprocess.stdin and encoding "buffer", noop transform', testGeneratorInputPipe, true, false, true, 'webTransform', [foobarBuffer, 'buffer']); -test('Can use webTransforms with subprocess.stdio[0] and encoding "hex", noop transform', testGeneratorInputPipe, false, false, true, 'webTransform', [foobarHex, 'hex']); -test('Can use webTransforms with subprocess.stdin and encoding "hex", noop transform', testGeneratorInputPipe, true, false, true, 'webTransform', [foobarHex, 'hex']); -test('Can use webTransforms with subprocess.stdio[0], objectMode, noop transform', testGeneratorInputPipe, false, true, true, 'webTransform', [foobarObject]); -test('Can use webTransforms with subprocess.stdin, objectMode, noop transform', testGeneratorInputPipe, true, true, true, 'webTransform', [foobarObject]); - -const testGeneratorStdioInputPipe = async (t, objectMode, addNoopTransform, type) => { - const {input, generators, output} = getInputObjectMode(objectMode, addNoopTransform, type); - const subprocess = execa('stdin-fd.js', ['3'], getStdio(3, [[], ...generators])); - subprocess.stdio[3].write(Array.isArray(input) ? input[0] : input); - const {stdout} = await subprocess; - t.is(stdout, output); -}; - -test('Can use generators with subprocess.stdio[*] as input', testGeneratorStdioInputPipe, false, false, 'generator'); -test('Can use generators with subprocess.stdio[*] as input, objectMode', testGeneratorStdioInputPipe, true, false, 'generator'); -test('Can use generators with subprocess.stdio[*] as input, noop transform', testGeneratorStdioInputPipe, false, true, 'generator'); -test('Can use generators with subprocess.stdio[*] as input, objectMode, noop transform', testGeneratorStdioInputPipe, true, true, 'generator'); -test('Can use duplexes with subprocess.stdio[*] as input', testGeneratorStdioInputPipe, false, false, 'duplex'); -test('Can use duplexes with subprocess.stdio[*] as input, objectMode', testGeneratorStdioInputPipe, true, false, 'duplex'); -test('Can use duplexes with subprocess.stdio[*] as input, noop transform', testGeneratorStdioInputPipe, false, true, 'duplex'); -test('Can use duplexes with subprocess.stdio[*] as input, objectMode, noop transform', testGeneratorStdioInputPipe, true, true, 'duplex'); -test('Can use webTransforms with subprocess.stdio[*] as input', testGeneratorStdioInputPipe, false, false, 'webTransform'); -test('Can use webTransforms with subprocess.stdio[*] as input, objectMode', testGeneratorStdioInputPipe, true, false, 'webTransform'); -test('Can use webTransforms with subprocess.stdio[*] as input, noop transform', testGeneratorStdioInputPipe, false, true, 'webTransform'); -test('Can use webTransforms with subprocess.stdio[*] as input, objectMode, noop transform', testGeneratorStdioInputPipe, true, true, 'webTransform'); diff --git a/test/transform/generator-main.js b/test/transform/generator-main.js deleted file mode 100644 index 24bfd360fc..0000000000 --- a/test/transform/generator-main.js +++ /dev/null @@ -1,185 +0,0 @@ -import {Buffer} from 'node:buffer'; -import {scheduler} from 'node:timers/promises'; -import test from 'ava'; -import {getStreamAsArray} from 'get-stream'; -import {execa, execaSync} from '../../index.js'; -import {foobarString} from '../helpers/input.js'; -import { - noopGenerator, - outputObjectGenerator, - convertTransformToFinal, - prefix, - suffix, -} from '../helpers/generator.js'; -import {generatorsMap} from '../helpers/map.js'; -import {defaultHighWaterMark} from '../helpers/stream.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {maxBuffer, assertErrorMessage} from '../helpers/max-buffer.js'; - -setFixtureDirectory(); - -const repeatCount = defaultHighWaterMark * 3; - -const writerGenerator = function * () { - for (let index = 0; index < repeatCount; index += 1) { - yield '\n'; - } -}; - -const getLengthGenerator = function * (t, chunk) { - t.is(chunk.length, 1); - yield chunk; -}; - -// eslint-disable-next-line max-params -const testHighWaterMark = async (t, passThrough, binary, objectMode, execaMethod) => { - const {stdout} = await execaMethod('noop.js', { - stdout: [ - ...(objectMode ? [outputObjectGenerator()] : []), - writerGenerator, - ...(passThrough ? [noopGenerator(false, binary)] : []), - {transform: getLengthGenerator.bind(undefined, t), preserveNewlines: true, objectMode: true}, - ], - }); - t.is(stdout.length, repeatCount); - t.true(stdout.every(chunk => chunk === '\n')); -}; - -test('Synchronous yields are not buffered, no passThrough', testHighWaterMark, false, false, false, execa); -test('Synchronous yields are not buffered, line-wise passThrough', testHighWaterMark, true, false, false, execa); -test('Synchronous yields are not buffered, binary passThrough', testHighWaterMark, true, true, false, execa); -test('Synchronous yields are not buffered, objectMode as input but not output', testHighWaterMark, false, false, true, execa); -test('Synchronous yields are not buffered, no passThrough, sync', testHighWaterMark, false, false, false, execaSync); -test('Synchronous yields are not buffered, line-wise passThrough, sync', testHighWaterMark, true, false, false, execaSync); -test('Synchronous yields are not buffered, binary passThrough, sync', testHighWaterMark, true, true, false, execaSync); -test('Synchronous yields are not buffered, objectMode as input but not output, sync', testHighWaterMark, false, false, true, execaSync); - -// eslint-disable-next-line max-params -const testNoYield = async (t, type, objectMode, final, output, execaMethod) => { - const {stdout} = await execaMethod('noop.js', {stdout: convertTransformToFinal(generatorsMap[type].noYield(objectMode), final)}); - t.deepEqual(stdout, output); -}; - -test('Generator can filter "transform" by not calling yield', testNoYield, 'generator', false, false, '', execa); -test('Generator can filter "transform" by not calling yield, objectMode', testNoYield, 'generator', true, false, [], execa); -test('Generator can filter "final" by not calling yield', testNoYield, 'generator', false, true, '', execa); -test('Generator can filter "final" by not calling yield, objectMode', testNoYield, 'generator', true, true, [], execa); -test('Generator can filter "transform" by not calling yield, sync', testNoYield, 'generator', false, false, '', execaSync); -test('Generator can filter "transform" by not calling yield, objectMode, sync', testNoYield, 'generator', true, false, [], execaSync); -test('Generator can filter "final" by not calling yield, sync', testNoYield, 'generator', false, true, '', execaSync); -test('Generator can filter "final" by not calling yield, objectMode, sync', testNoYield, 'generator', true, true, [], execaSync); -test('Duplex can filter by not calling push', testNoYield, 'duplex', false, false, '', execa); -test('Duplex can filter by not calling push, objectMode', testNoYield, 'duplex', true, false, [], execa); -test('WebTransform can filter by not calling push', testNoYield, 'webTransform', false, false, '', execa); -test('WebTransform can filter by not calling push, objectMode', testNoYield, 'webTransform', true, false, [], execa); - -const testMultipleYields = async (t, type, final, binary) => { - const {stdout} = await execa('noop-fd.js', ['1', foobarString], {stdout: convertTransformToFinal(generatorsMap[type].multipleYield(), final)}); - const newline = binary ? '' : '\n'; - t.is(stdout, `${prefix}${newline}${foobarString}${newline}${suffix}`); -}; - -test('Generator can yield "transform" multiple times at different moments', testMultipleYields, 'generator', false, false); -test('Generator can yield "final" multiple times at different moments', testMultipleYields, 'generator', true, false); -test('Duplex can push multiple times at different moments', testMultipleYields, 'duplex', false, true); -test('WebTransform can push multiple times at different moments', testMultipleYields, 'webTransform', false, true); - -const partsPerChunk = 4; -const chunksPerCall = 10; -const callCount = 5; -const fullString = '\n'.repeat(defaultHighWaterMark / partsPerChunk); - -const yieldFullStrings = function * () { - yield * Array.from({length: partsPerChunk * chunksPerCall}).fill(fullString); -}; - -const manyYieldGenerator = async function * () { - for (let index = 0; index < callCount; index += 1) { - yield * yieldFullStrings(); - // eslint-disable-next-line no-await-in-loop - await scheduler.yield(); - } -}; - -const testManyYields = async (t, final) => { - const subprocess = execa('noop.js', {stdout: convertTransformToFinal(manyYieldGenerator, final), stripFinalNewline: false}); - const [chunks, {stdout}] = await Promise.all([getStreamAsArray(subprocess.stdout), subprocess]); - const expectedChunk = Buffer.from(fullString); - t.deepEqual(chunks, Array.from({length: callCount * partsPerChunk * chunksPerCall}).fill(expectedChunk)); - t.is(chunks.join(''), stdout); -}; - -test('Generator "transform" yields are sent right away', testManyYields, false); -test('Generator "final" yields are sent right away', testManyYields, true); - -const testMaxBuffer = async (t, type) => { - const bigString = '.'.repeat(maxBuffer); - const {stdout} = await execa('noop.js', { - maxBuffer, - stdout: generatorsMap[type].getOutput(bigString)(false, true), - }); - t.is(stdout, bigString); - - const {isMaxBuffer, shortMessage} = await t.throwsAsync(execa('noop.js', { - maxBuffer, - stdout: generatorsMap[type].getOutput(`${bigString}.`)(false, true), - })); - t.true(isMaxBuffer); - assertErrorMessage(t, shortMessage); -}; - -test('Generators take "maxBuffer" into account', testMaxBuffer, 'generator'); -test('Duplexes take "maxBuffer" into account', testMaxBuffer, 'duplex'); -test('WebTransforms take "maxBuffer" into account', testMaxBuffer, 'webTransform'); - -test('Generators does not take "maxBuffer" into account, sync', t => { - const bigString = '.'.repeat(maxBuffer); - const {isMaxBuffer, stdout} = execaSync('noop.js', { - maxBuffer, - stdout: generatorsMap.generator.getOutput(`${bigString}.`)(false, true), - }); - t.false(isMaxBuffer); - t.is(stdout.length, maxBuffer + 1); -}); - -const testMaxBufferObject = async (t, type) => { - const bigArray = Array.from({length: maxBuffer}).fill('..'); - const {stdout} = await execa('noop.js', { - maxBuffer, - stdout: generatorsMap[type].getOutputs(bigArray)(true, true), - }); - t.is(stdout.length, maxBuffer); - - const {isMaxBuffer, shortMessage} = await t.throwsAsync(execa('noop.js', { - maxBuffer, - stdout: generatorsMap[type].getOutputs([...bigArray, ''])(true, true), - })); - t.true(isMaxBuffer); - assertErrorMessage(t, shortMessage, {unit: 'objects'}); -}; - -test('Generators take "maxBuffer" into account, objectMode', testMaxBufferObject, 'generator'); -test('Duplexes take "maxBuffer" into account, objectMode', testMaxBufferObject, 'duplex'); -test('WebTransforms take "maxBuffer" into account, objectMode', testMaxBufferObject, 'webTransform'); - -test('Generators does not take "maxBuffer" into account, objectMode, sync', t => { - const bigArray = Array.from({length: maxBuffer}).fill('..'); - const {isMaxBuffer, stdout} = execaSync('noop.js', { - maxBuffer, - stdout: generatorsMap.generator.getOutputs([...bigArray, ''])(true, true), - }); - t.false(isMaxBuffer); - t.is(stdout.length, maxBuffer + 1); -}); - -const testAsyncGenerators = async (t, type, final) => { - const {stdout} = await execa('noop.js', { - stdout: convertTransformToFinal(generatorsMap[type].timeout(1e2)(), final), - }); - t.is(stdout, foobarString); -}; - -test('Generators "transform" is awaited on success', testAsyncGenerators, 'generator', false); -test('Generators "final" is awaited on success', testAsyncGenerators, 'generator', true); -test('Duplex is awaited on success', testAsyncGenerators, 'duplex', false); -test('WebTransform is awaited on success', testAsyncGenerators, 'webTransform', false); diff --git a/test/transform/generator-mixed.js b/test/transform/generator-mixed.js deleted file mode 100644 index 5cd80fbad3..0000000000 --- a/test/transform/generator-mixed.js +++ /dev/null @@ -1,104 +0,0 @@ -import {readFile, writeFile, rm} from 'node:fs/promises'; -import {PassThrough} from 'node:stream'; -import test from 'ava'; -import getStream from 'get-stream'; -import tempfile from 'tempfile'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString, foobarUppercase, foobarUint8Array} from '../helpers/input.js'; -import {uppercaseGenerator} from '../helpers/generator.js'; -import {uppercaseBufferDuplex} from '../helpers/duplex.js'; -import {uppercaseBufferWebTransform} from '../helpers/web-transform.js'; -import {generatorsMap} from '../helpers/map.js'; - -setFixtureDirectory(); - -const testInputOption = async (t, type, execaMethod) => { - const {stdout} = await execaMethod('stdin-fd.js', ['0'], {stdin: generatorsMap[type].uppercase(), input: foobarUint8Array}); - t.is(stdout, foobarUppercase); -}; - -test('Can use generators with input option', testInputOption, 'generator', execa); -test('Can use generators with input option, sync', testInputOption, 'generator', execaSync); -test('Can use duplexes with input option', testInputOption, 'duplex', execa); -test('Can use webTransforms with input option', testInputOption, 'webTransform', execa); - -// eslint-disable-next-line max-params -const testInputFile = async (t, stdinOption, useInputFile, reversed, execaMethod) => { - const filePath = tempfile(); - await writeFile(filePath, foobarString); - const options = useInputFile - ? {inputFile: filePath, stdin: stdinOption} - : {stdin: [{file: filePath}, stdinOption]}; - options.stdin = reversed ? options.stdin.reverse() : options.stdin; - const {stdout} = await execaMethod('stdin-fd.js', ['0'], options); - t.is(stdout, foobarUppercase); - await rm(filePath); -}; - -test('Can use generators with a file as input', testInputFile, uppercaseGenerator(), false, false, execa); -test('Can use generators with a file as input, reversed', testInputFile, uppercaseGenerator(), false, true, execa); -test('Can use generators with inputFile option', testInputFile, uppercaseGenerator(), true, false, execa); -test('Can use generators with a file as input, sync', testInputFile, uppercaseGenerator(), false, false, execaSync); -test('Can use generators with a file as input, reversed, sync', testInputFile, uppercaseGenerator(), false, true, execaSync); -test('Can use generators with inputFile option, sync', testInputFile, uppercaseGenerator(), true, false, execaSync); -test('Can use duplexes with a file as input', testInputFile, uppercaseBufferDuplex(), false, false, execa); -test('Can use duplexes with a file as input, reversed', testInputFile, uppercaseBufferDuplex(), false, true, execa); -test('Can use duplexes with inputFile option', testInputFile, uppercaseBufferDuplex(), true, false, execa); -test('Can use webTransforms with a file as input', testInputFile, uppercaseBufferWebTransform(), false, false, execa); -test('Can use webTransforms with a file as input, reversed', testInputFile, uppercaseBufferWebTransform(), false, true, execa); -test('Can use webTransforms with inputFile option', testInputFile, uppercaseBufferWebTransform(), true, false, execa); - -const testOutputFile = async (t, reversed, type, execaMethod) => { - const filePath = tempfile(); - const stdoutOption = [generatorsMap[type].uppercaseBuffer(false, true), {file: filePath}]; - const reversedStdoutOption = reversed ? stdoutOption.reverse() : stdoutOption; - const {stdout} = await execaMethod('noop-fd.js', ['1'], {stdout: reversedStdoutOption}); - t.is(stdout, foobarUppercase); - t.is(await readFile(filePath, 'utf8'), foobarUppercase); - await rm(filePath); -}; - -test('Can use generators with a file as output', testOutputFile, false, 'generator', execa); -test('Can use generators with a file as output, reversed', testOutputFile, true, 'generator', execa); -test('Can use generators with a file as output, sync', testOutputFile, false, 'generator', execaSync); -test('Can use generators with a file as output, reversed, sync', testOutputFile, true, 'generator', execaSync); -test('Can use duplexes with a file as output', testOutputFile, false, 'duplex', execa); -test('Can use duplexes with a file as output, reversed', testOutputFile, true, 'duplex', execa); -test('Can use webTransforms with a file as output', testOutputFile, false, 'webTransform', execa); -test('Can use webTransforms with a file as output, reversed', testOutputFile, true, 'webTransform', execa); - -const testWritableDestination = async (t, type) => { - const passThrough = new PassThrough(); - const [{stdout}, streamOutput] = await Promise.all([ - execa('noop-fd.js', ['1', foobarString], {stdout: [generatorsMap[type].uppercaseBuffer(false, true), passThrough]}), - getStream(passThrough), - ]); - t.is(stdout, foobarUppercase); - t.is(streamOutput, foobarUppercase); -}; - -test('Can use generators to a Writable stream', testWritableDestination, 'generator'); -test('Can use duplexes to a Writable stream', testWritableDestination, 'duplex'); -test('Can use webTransforms to a Writable stream', testWritableDestination, 'webTransform'); - -const testReadableSource = async (t, type) => { - const passThrough = new PassThrough(); - const subprocess = execa('stdin-fd.js', ['0'], {stdin: [passThrough, generatorsMap[type].uppercase()]}); - passThrough.end(foobarString); - const {stdout} = await subprocess; - t.is(stdout, foobarUppercase); -}; - -test('Can use generators from a Readable stream', testReadableSource, 'generator'); -test('Can use duplexes from a Readable stream', testReadableSource, 'duplex'); -test('Can use webTransforms from a Readable stream', testReadableSource, 'webTransform'); - -const testInherit = async (t, type) => { - const {stdout} = await execa('nested-inherit.js', [type]); - t.is(stdout, foobarUppercase); -}; - -test('Can use generators with "inherit"', testInherit, 'generator'); -test('Can use duplexes with "inherit"', testInherit, 'duplex'); -test('Can use webTransforms with "inherit"', testInherit, 'webTransform'); diff --git a/test/transform/generator-output.js b/test/transform/generator-output.js deleted file mode 100644 index 89b118637b..0000000000 --- a/test/transform/generator-output.js +++ /dev/null @@ -1,261 +0,0 @@ -import test from 'ava'; -import getStream, {getStreamAsArray} from 'get-stream'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {getStdio} from '../helpers/stdio.js'; -import {foobarString, foobarUppercase, foobarObject} from '../helpers/input.js'; -import {generatorsMap} from '../helpers/map.js'; - -setFixtureDirectory(); - -const getOutputObjectMode = (objectMode, addNoopTransform, type, binary) => objectMode - ? { - generators: generatorsMap[type].addNoop(generatorsMap[type].outputObject(), addNoopTransform, objectMode, binary), - output: [foobarObject], - getStreamMethod: getStreamAsArray, - } - : { - generators: generatorsMap[type].addNoop(generatorsMap[type].uppercaseBuffer(objectMode, true), addNoopTransform, objectMode, binary), - output: foobarUppercase, - getStreamMethod: getStream, - }; - -// eslint-disable-next-line max-params -const testGeneratorOutput = async (t, fdNumber, reject, useShortcutProperty, objectMode, addNoopTransform, type, execaMethod) => { - const {generators, output} = getOutputObjectMode(objectMode, addNoopTransform, type); - const fixtureName = reject ? 'noop-fd.js' : 'noop-fail.js'; - const {stdout, stderr, stdio} = await execaMethod(fixtureName, [`${fdNumber}`, foobarString], {...getStdio(fdNumber, generators), reject}); - const result = useShortcutProperty ? [stdout, stderr][fdNumber - 1] : stdio[fdNumber]; - t.deepEqual(result, output); -}; - -test('Can use generators with result.stdio[1]', testGeneratorOutput, 1, true, false, false, false, 'generator', execa); -test('Can use generators with result.stdout', testGeneratorOutput, 1, true, true, false, false, 'generator', execa); -test('Can use generators with result.stdio[2]', testGeneratorOutput, 2, true, false, false, false, 'generator', execa); -test('Can use generators with result.stderr', testGeneratorOutput, 2, true, true, false, false, 'generator', execa); -test('Can use generators with result.stdio[*] as output', testGeneratorOutput, 3, true, false, false, false, 'generator', execa); -test('Can use generators with error.stdio[1]', testGeneratorOutput, 1, false, false, false, false, 'generator', execa); -test('Can use generators with error.stdout', testGeneratorOutput, 1, false, true, false, false, 'generator', execa); -test('Can use generators with error.stdio[2]', testGeneratorOutput, 2, false, false, false, false, 'generator', execa); -test('Can use generators with error.stderr', testGeneratorOutput, 2, false, true, false, false, 'generator', execa); -test('Can use generators with error.stdio[*] as output', testGeneratorOutput, 3, false, false, false, false, 'generator', execa); -test('Can use generators with result.stdio[1], objectMode', testGeneratorOutput, 1, true, false, true, false, 'generator', execa); -test('Can use generators with result.stdout, objectMode', testGeneratorOutput, 1, true, true, true, false, 'generator', execa); -test('Can use generators with result.stdio[2], objectMode', testGeneratorOutput, 2, true, false, true, false, 'generator', execa); -test('Can use generators with result.stderr, objectMode', testGeneratorOutput, 2, true, true, true, false, 'generator', execa); -test('Can use generators with result.stdio[*] as output, objectMode', testGeneratorOutput, 3, true, false, true, false, 'generator', execa); -test('Can use generators with error.stdio[1], objectMode', testGeneratorOutput, 1, false, false, true, false, 'generator', execa); -test('Can use generators with error.stdout, objectMode', testGeneratorOutput, 1, false, true, true, false, 'generator', execa); -test('Can use generators with error.stdio[2], objectMode', testGeneratorOutput, 2, false, false, true, false, 'generator', execa); -test('Can use generators with error.stderr, objectMode', testGeneratorOutput, 2, false, true, true, false, 'generator', execa); -test('Can use generators with error.stdio[*] as output, objectMode', testGeneratorOutput, 3, false, false, true, false, 'generator', execa); -test('Can use generators with result.stdio[1], noop transform', testGeneratorOutput, 1, true, false, false, true, 'generator', execa); -test('Can use generators with result.stdout, noop transform', testGeneratorOutput, 1, true, true, false, true, 'generator', execa); -test('Can use generators with result.stdio[2], noop transform', testGeneratorOutput, 2, true, false, false, true, 'generator', execa); -test('Can use generators with result.stderr, noop transform', testGeneratorOutput, 2, true, true, false, true, 'generator', execa); -test('Can use generators with result.stdio[*] as output, noop transform', testGeneratorOutput, 3, true, false, false, true, 'generator', execa); -test('Can use generators with error.stdio[1], noop transform', testGeneratorOutput, 1, false, false, false, true, 'generator', execa); -test('Can use generators with error.stdout, noop transform', testGeneratorOutput, 1, false, true, false, true, 'generator', execa); -test('Can use generators with error.stdio[2], noop transform', testGeneratorOutput, 2, false, false, false, true, 'generator', execa); -test('Can use generators with error.stderr, noop transform', testGeneratorOutput, 2, false, true, false, true, 'generator', execa); -test('Can use generators with error.stdio[*] as output, noop transform', testGeneratorOutput, 3, false, false, false, true, 'generator', execa); -test('Can use generators with result.stdio[1], objectMode, noop transform', testGeneratorOutput, 1, true, false, true, true, 'generator', execa); -test('Can use generators with result.stdout, objectMode, noop transform', testGeneratorOutput, 1, true, true, true, true, 'generator', execa); -test('Can use generators with result.stdio[2], objectMode, noop transform', testGeneratorOutput, 2, true, false, true, true, 'generator', execa); -test('Can use generators with result.stderr, objectMode, noop transform', testGeneratorOutput, 2, true, true, true, true, 'generator', execa); -test('Can use generators with result.stdio[*] as output, objectMode, noop transform', testGeneratorOutput, 3, true, false, true, true, 'generator', execa); -test('Can use generators with error.stdio[1], objectMode, noop transform', testGeneratorOutput, 1, false, false, true, true, 'generator', execa); -test('Can use generators with error.stdout, objectMode, noop transform', testGeneratorOutput, 1, false, true, true, true, 'generator', execa); -test('Can use generators with error.stdio[2], objectMode, noop transform', testGeneratorOutput, 2, false, false, true, true, 'generator', execa); -test('Can use generators with error.stderr, objectMode, noop transform', testGeneratorOutput, 2, false, true, true, true, 'generator', execa); -test('Can use generators with error.stdio[*] as output, objectMode, noop transform', testGeneratorOutput, 3, false, false, true, true, 'generator', execa); -test('Can use generators with result.stdio[1], sync', testGeneratorOutput, 1, true, false, false, false, 'generator', execaSync); -test('Can use generators with result.stdout, sync', testGeneratorOutput, 1, true, true, false, false, 'generator', execaSync); -test('Can use generators with result.stdio[2], sync', testGeneratorOutput, 2, true, false, false, false, 'generator', execaSync); -test('Can use generators with result.stderr, sync', testGeneratorOutput, 2, true, true, false, false, 'generator', execaSync); -test('Can use generators with result.stdio[*] as output, sync', testGeneratorOutput, 3, true, false, false, false, 'generator', execaSync); -test('Can use generators with error.stdio[1], sync', testGeneratorOutput, 1, false, false, false, false, 'generator', execaSync); -test('Can use generators with error.stdout, sync', testGeneratorOutput, 1, false, true, false, false, 'generator', execaSync); -test('Can use generators with error.stdio[2], sync', testGeneratorOutput, 2, false, false, false, false, 'generator', execaSync); -test('Can use generators with error.stderr, sync', testGeneratorOutput, 2, false, true, false, false, 'generator', execaSync); -test('Can use generators with error.stdio[*] as output, sync', testGeneratorOutput, 3, false, false, false, false, 'generator', execaSync); -test('Can use generators with result.stdio[1], objectMode, sync', testGeneratorOutput, 1, true, false, true, false, 'generator', execaSync); -test('Can use generators with result.stdout, objectMode, sync', testGeneratorOutput, 1, true, true, true, false, 'generator', execaSync); -test('Can use generators with result.stdio[2], objectMode, sync', testGeneratorOutput, 2, true, false, true, false, 'generator', execaSync); -test('Can use generators with result.stderr, objectMode, sync', testGeneratorOutput, 2, true, true, true, false, 'generator', execaSync); -test('Can use generators with result.stdio[*] as output, objectMode, sync', testGeneratorOutput, 3, true, false, true, false, 'generator', execaSync); -test('Can use generators with error.stdio[1], objectMode, sync', testGeneratorOutput, 1, false, false, true, false, 'generator', execaSync); -test('Can use generators with error.stdout, objectMode, sync', testGeneratorOutput, 1, false, true, true, false, 'generator', execaSync); -test('Can use generators with error.stdio[2], objectMode, sync', testGeneratorOutput, 2, false, false, true, false, 'generator', execaSync); -test('Can use generators with error.stderr, objectMode, sync', testGeneratorOutput, 2, false, true, true, false, 'generator', execaSync); -test('Can use generators with error.stdio[*] as output, objectMode, sync', testGeneratorOutput, 3, false, false, true, false, 'generator', execaSync); -test('Can use generators with result.stdio[1], noop transform, sync', testGeneratorOutput, 1, true, false, false, true, 'generator', execaSync); -test('Can use generators with result.stdout, noop transform, sync', testGeneratorOutput, 1, true, true, false, true, 'generator', execaSync); -test('Can use generators with result.stdio[2], noop transform, sync', testGeneratorOutput, 2, true, false, false, true, 'generator', execaSync); -test('Can use generators with result.stderr, noop transform, sync', testGeneratorOutput, 2, true, true, false, true, 'generator', execaSync); -test('Can use generators with result.stdio[*] as output, noop transform, sync', testGeneratorOutput, 3, true, false, false, true, 'generator', execaSync); -test('Can use generators with error.stdio[1], noop transform, sync', testGeneratorOutput, 1, false, false, false, true, 'generator', execaSync); -test('Can use generators with error.stdout, noop transform, sync', testGeneratorOutput, 1, false, true, false, true, 'generator', execaSync); -test('Can use generators with error.stdio[2], noop transform, sync', testGeneratorOutput, 2, false, false, false, true, 'generator', execaSync); -test('Can use generators with error.stderr, noop transform, sync', testGeneratorOutput, 2, false, true, false, true, 'generator', execaSync); -test('Can use generators with error.stdio[*] as output, noop transform, sync', testGeneratorOutput, 3, false, false, false, true, 'generator', execaSync); -test('Can use generators with result.stdio[1], objectMode, noop transform, sync', testGeneratorOutput, 1, true, false, true, true, 'generator', execaSync); -test('Can use generators with result.stdout, objectMode, noop transform, sync', testGeneratorOutput, 1, true, true, true, true, 'generator', execaSync); -test('Can use generators with result.stdio[2], objectMode, noop transform, sync', testGeneratorOutput, 2, true, false, true, true, 'generator', execaSync); -test('Can use generators with result.stderr, objectMode, noop transform, sync', testGeneratorOutput, 2, true, true, true, true, 'generator', execaSync); -test('Can use generators with result.stdio[*] as output, objectMode, noop transform, sync', testGeneratorOutput, 3, true, false, true, true, 'generator', execaSync); -test('Can use generators with error.stdio[1], objectMode, noop transform, sync', testGeneratorOutput, 1, false, false, true, true, 'generator', execaSync); -test('Can use generators with error.stdout, objectMode, noop transform, sync', testGeneratorOutput, 1, false, true, true, true, 'generator', execaSync); -test('Can use generators with error.stdio[2], objectMode, noop transform, sync', testGeneratorOutput, 2, false, false, true, true, 'generator', execaSync); -test('Can use generators with error.stderr, objectMode, noop transform, sync', testGeneratorOutput, 2, false, true, true, true, 'generator', execaSync); -test('Can use generators with error.stdio[*] as output, objectMode, noop transform, sync', testGeneratorOutput, 3, false, false, true, true, 'generator', execaSync); -test('Can use duplexes with result.stdio[1]', testGeneratorOutput, 1, true, false, false, false, 'duplex', execa); -test('Can use duplexes with result.stdout', testGeneratorOutput, 1, true, true, false, false, 'duplex', execa); -test('Can use duplexes with result.stdio[2]', testGeneratorOutput, 2, true, false, false, false, 'duplex', execa); -test('Can use duplexes with result.stderr', testGeneratorOutput, 2, true, true, false, false, 'duplex', execa); -test('Can use duplexes with result.stdio[*] as output', testGeneratorOutput, 3, true, false, false, false, 'duplex', execa); -test('Can use duplexes with error.stdio[1]', testGeneratorOutput, 1, false, false, false, false, 'duplex', execa); -test('Can use duplexes with error.stdout', testGeneratorOutput, 1, false, true, false, false, 'duplex', execa); -test('Can use duplexes with error.stdio[2]', testGeneratorOutput, 2, false, false, false, false, 'duplex', execa); -test('Can use duplexes with error.stderr', testGeneratorOutput, 2, false, true, false, false, 'duplex', execa); -test('Can use duplexes with error.stdio[*] as output', testGeneratorOutput, 3, false, false, false, false, 'duplex', execa); -test('Can use duplexes with result.stdio[1], objectMode', testGeneratorOutput, 1, true, false, true, false, 'duplex', execa); -test('Can use duplexes with result.stdout, objectMode', testGeneratorOutput, 1, true, true, true, false, 'duplex', execa); -test('Can use duplexes with result.stdio[2], objectMode', testGeneratorOutput, 2, true, false, true, false, 'duplex', execa); -test('Can use duplexes with result.stderr, objectMode', testGeneratorOutput, 2, true, true, true, false, 'duplex', execa); -test('Can use duplexes with result.stdio[*] as output, objectMode', testGeneratorOutput, 3, true, false, true, false, 'duplex', execa); -test('Can use duplexes with error.stdio[1], objectMode', testGeneratorOutput, 1, false, false, true, false, 'duplex', execa); -test('Can use duplexes with error.stdout, objectMode', testGeneratorOutput, 1, false, true, true, false, 'duplex', execa); -test('Can use duplexes with error.stdio[2], objectMode', testGeneratorOutput, 2, false, false, true, false, 'duplex', execa); -test('Can use duplexes with error.stderr, objectMode', testGeneratorOutput, 2, false, true, true, false, 'duplex', execa); -test('Can use duplexes with error.stdio[*] as output, objectMode', testGeneratorOutput, 3, false, false, true, false, 'duplex', execa); -test('Can use duplexes with result.stdio[1], noop transform', testGeneratorOutput, 1, true, false, false, true, 'duplex', execa); -test('Can use duplexes with result.stdout, noop transform', testGeneratorOutput, 1, true, true, false, true, 'duplex', execa); -test('Can use duplexes with result.stdio[2], noop transform', testGeneratorOutput, 2, true, false, false, true, 'duplex', execa); -test('Can use duplexes with result.stderr, noop transform', testGeneratorOutput, 2, true, true, false, true, 'duplex', execa); -test('Can use duplexes with result.stdio[*] as output, noop transform', testGeneratorOutput, 3, true, false, false, true, 'duplex', execa); -test('Can use duplexes with error.stdio[1], noop transform', testGeneratorOutput, 1, false, false, false, true, 'duplex', execa); -test('Can use duplexes with error.stdout, noop transform', testGeneratorOutput, 1, false, true, false, true, 'duplex', execa); -test('Can use duplexes with error.stdio[2], noop transform', testGeneratorOutput, 2, false, false, false, true, 'duplex', execa); -test('Can use duplexes with error.stderr, noop transform', testGeneratorOutput, 2, false, true, false, true, 'duplex', execa); -test('Can use duplexes with error.stdio[*] as output, noop transform', testGeneratorOutput, 3, false, false, false, true, 'duplex', execa); -test('Can use duplexes with result.stdio[1], objectMode, noop transform', testGeneratorOutput, 1, true, false, true, true, 'duplex', execa); -test('Can use duplexes with result.stdout, objectMode, noop transform', testGeneratorOutput, 1, true, true, true, true, 'duplex', execa); -test('Can use duplexes with result.stdio[2], objectMode, noop transform', testGeneratorOutput, 2, true, false, true, true, 'duplex', execa); -test('Can use duplexes with result.stderr, objectMode, noop transform', testGeneratorOutput, 2, true, true, true, true, 'duplex', execa); -test('Can use duplexes with result.stdio[*] as output, objectMode, noop transform', testGeneratorOutput, 3, true, false, true, true, 'duplex', execa); -test('Can use duplexes with error.stdio[1], objectMode, noop transform', testGeneratorOutput, 1, false, false, true, true, 'duplex', execa); -test('Can use duplexes with error.stdout, objectMode, noop transform', testGeneratorOutput, 1, false, true, true, true, 'duplex', execa); -test('Can use duplexes with error.stdio[2], objectMode, noop transform', testGeneratorOutput, 2, false, false, true, true, 'duplex', execa); -test('Can use duplexes with error.stderr, objectMode, noop transform', testGeneratorOutput, 2, false, true, true, true, 'duplex', execa); -test('Can use duplexes with error.stdio[*] as output, objectMode, noop transform', testGeneratorOutput, 3, false, false, true, true, 'duplex', execa); -test('Can use webTransforms with result.stdio[1]', testGeneratorOutput, 1, true, false, false, false, 'webTransform', execa); -test('Can use webTransforms with result.stdout', testGeneratorOutput, 1, true, true, false, false, 'webTransform', execa); -test('Can use webTransforms with result.stdio[2]', testGeneratorOutput, 2, true, false, false, false, 'webTransform', execa); -test('Can use webTransforms with result.stderr', testGeneratorOutput, 2, true, true, false, false, 'webTransform', execa); -test('Can use webTransforms with result.stdio[*] as output', testGeneratorOutput, 3, true, false, false, false, 'webTransform', execa); -test('Can use webTransforms with error.stdio[1]', testGeneratorOutput, 1, false, false, false, false, 'webTransform', execa); -test('Can use webTransforms with error.stdout', testGeneratorOutput, 1, false, true, false, false, 'webTransform', execa); -test('Can use webTransforms with error.stdio[2]', testGeneratorOutput, 2, false, false, false, false, 'webTransform', execa); -test('Can use webTransforms with error.stderr', testGeneratorOutput, 2, false, true, false, false, 'webTransform', execa); -test('Can use webTransforms with error.stdio[*] as output', testGeneratorOutput, 3, false, false, false, false, 'webTransform', execa); -test('Can use webTransforms with result.stdio[1], objectMode', testGeneratorOutput, 1, true, false, true, false, 'webTransform', execa); -test('Can use webTransforms with result.stdout, objectMode', testGeneratorOutput, 1, true, true, true, false, 'webTransform', execa); -test('Can use webTransforms with result.stdio[2], objectMode', testGeneratorOutput, 2, true, false, true, false, 'webTransform', execa); -test('Can use webTransforms with result.stderr, objectMode', testGeneratorOutput, 2, true, true, true, false, 'webTransform', execa); -test('Can use webTransforms with result.stdio[*] as output, objectMode', testGeneratorOutput, 3, true, false, true, false, 'webTransform', execa); -test('Can use webTransforms with error.stdio[1], objectMode', testGeneratorOutput, 1, false, false, true, false, 'webTransform', execa); -test('Can use webTransforms with error.stdout, objectMode', testGeneratorOutput, 1, false, true, true, false, 'webTransform', execa); -test('Can use webTransforms with error.stdio[2], objectMode', testGeneratorOutput, 2, false, false, true, false, 'webTransform', execa); -test('Can use webTransforms with error.stderr, objectMode', testGeneratorOutput, 2, false, true, true, false, 'webTransform', execa); -test('Can use webTransforms with error.stdio[*] as output, objectMode', testGeneratorOutput, 3, false, false, true, false, 'webTransform', execa); -test('Can use webTransforms with result.stdio[1], noop transform', testGeneratorOutput, 1, true, false, false, true, 'webTransform', execa); -test('Can use webTransforms with result.stdout, noop transform', testGeneratorOutput, 1, true, true, false, true, 'webTransform', execa); -test('Can use webTransforms with result.stdio[2], noop transform', testGeneratorOutput, 2, true, false, false, true, 'webTransform', execa); -test('Can use webTransforms with result.stderr, noop transform', testGeneratorOutput, 2, true, true, false, true, 'webTransform', execa); -test('Can use webTransforms with result.stdio[*] as output, noop transform', testGeneratorOutput, 3, true, false, false, true, 'webTransform', execa); -test('Can use webTransforms with error.stdio[1], noop transform', testGeneratorOutput, 1, false, false, false, true, 'webTransform', execa); -test('Can use webTransforms with error.stdout, noop transform', testGeneratorOutput, 1, false, true, false, true, 'webTransform', execa); -test('Can use webTransforms with error.stdio[2], noop transform', testGeneratorOutput, 2, false, false, false, true, 'webTransform', execa); -test('Can use webTransforms with error.stderr, noop transform', testGeneratorOutput, 2, false, true, false, true, 'webTransform', execa); -test('Can use webTransforms with error.stdio[*] as output, noop transform', testGeneratorOutput, 3, false, false, false, true, 'webTransform', execa); -test('Can use webTransforms with result.stdio[1], objectMode, noop transform', testGeneratorOutput, 1, true, false, true, true, 'webTransform', execa); -test('Can use webTransforms with result.stdout, objectMode, noop transform', testGeneratorOutput, 1, true, true, true, true, 'webTransform', execa); -test('Can use webTransforms with result.stdio[2], objectMode, noop transform', testGeneratorOutput, 2, true, false, true, true, 'webTransform', execa); -test('Can use webTransforms with result.stderr, objectMode, noop transform', testGeneratorOutput, 2, true, true, true, true, 'webTransform', execa); -test('Can use webTransforms with result.stdio[*] as output, objectMode, noop transform', testGeneratorOutput, 3, true, false, true, true, 'webTransform', execa); -test('Can use webTransforms with error.stdio[1], objectMode, noop transform', testGeneratorOutput, 1, false, false, true, true, 'webTransform', execa); -test('Can use webTransforms with error.stdout, objectMode, noop transform', testGeneratorOutput, 1, false, true, true, true, 'webTransform', execa); -test('Can use webTransforms with error.stdio[2], objectMode, noop transform', testGeneratorOutput, 2, false, false, true, true, 'webTransform', execa); -test('Can use webTransforms with error.stderr, objectMode, noop transform', testGeneratorOutput, 2, false, true, true, true, 'webTransform', execa); -test('Can use webTransforms with error.stdio[*] as output, objectMode, noop transform', testGeneratorOutput, 3, false, false, true, true, 'webTransform', execa); - -// eslint-disable-next-line max-params -const testGeneratorOutputPipe = async (t, fdNumber, useShortcutProperty, objectMode, addNoopTransform, type) => { - const {generators, output, getStreamMethod} = getOutputObjectMode(objectMode, addNoopTransform, type, true); - const subprocess = execa('noop-fd.js', [`${fdNumber}`, foobarString], getStdio(fdNumber, generators)); - const stream = useShortcutProperty ? [subprocess.stdout, subprocess.stderr][fdNumber - 1] : subprocess.stdio[fdNumber]; - const [result] = await Promise.all([getStreamMethod(stream), subprocess]); - t.deepEqual(result, output); -}; - -test('Can use generators with subprocess.stdio[1]', testGeneratorOutputPipe, 1, false, false, false, 'generator'); -test('Can use generators with subprocess.stdout', testGeneratorOutputPipe, 1, true, false, false, 'generator'); -test('Can use generators with subprocess.stdio[2]', testGeneratorOutputPipe, 2, false, false, false, 'generator'); -test('Can use generators with subprocess.stderr', testGeneratorOutputPipe, 2, true, false, false, 'generator'); -test('Can use generators with subprocess.stdio[*] as output', testGeneratorOutputPipe, 3, false, false, false, 'generator'); -test('Can use generators with subprocess.stdio[1], objectMode', testGeneratorOutputPipe, 1, false, true, false, 'generator'); -test('Can use generators with subprocess.stdout, objectMode', testGeneratorOutputPipe, 1, true, true, false, 'generator'); -test('Can use generators with subprocess.stdio[2], objectMode', testGeneratorOutputPipe, 2, false, true, false, 'generator'); -test('Can use generators with subprocess.stderr, objectMode', testGeneratorOutputPipe, 2, true, true, false, 'generator'); -test('Can use generators with subprocess.stdio[*] as output, objectMode', testGeneratorOutputPipe, 3, false, true, false, 'generator'); -test('Can use generators with subprocess.stdio[1], noop transform', testGeneratorOutputPipe, 1, false, false, true, 'generator'); -test('Can use generators with subprocess.stdout, noop transform', testGeneratorOutputPipe, 1, true, false, true, 'generator'); -test('Can use generators with subprocess.stdio[2], noop transform', testGeneratorOutputPipe, 2, false, false, true, 'generator'); -test('Can use generators with subprocess.stderr, noop transform', testGeneratorOutputPipe, 2, true, false, true, 'generator'); -test('Can use generators with subprocess.stdio[*] as output, noop transform', testGeneratorOutputPipe, 3, false, false, true, 'generator'); -test('Can use generators with subprocess.stdio[1], objectMode, noop transform', testGeneratorOutputPipe, 1, false, true, true, 'generator'); -test('Can use generators with subprocess.stdout, objectMode, noop transform', testGeneratorOutputPipe, 1, true, true, true, 'generator'); -test('Can use generators with subprocess.stdio[2], objectMode, noop transform', testGeneratorOutputPipe, 2, false, true, true, 'generator'); -test('Can use generators with subprocess.stderr, objectMode, noop transform', testGeneratorOutputPipe, 2, true, true, true, 'generator'); -test('Can use generators with subprocess.stdio[*] as output, objectMode, noop transform', testGeneratorOutputPipe, 3, false, true, true, 'generator'); -test('Can use duplexes with subprocess.stdio[1]', testGeneratorOutputPipe, 1, false, false, false, 'duplex'); -test('Can use duplexes with subprocess.stdout', testGeneratorOutputPipe, 1, true, false, false, 'duplex'); -test('Can use duplexes with subprocess.stdio[2]', testGeneratorOutputPipe, 2, false, false, false, 'duplex'); -test('Can use duplexes with subprocess.stderr', testGeneratorOutputPipe, 2, true, false, false, 'duplex'); -test('Can use duplexes with subprocess.stdio[*] as output', testGeneratorOutputPipe, 3, false, false, false, 'duplex'); -test('Can use duplexes with subprocess.stdio[1], objectMode', testGeneratorOutputPipe, 1, false, true, false, 'duplex'); -test('Can use duplexes with subprocess.stdout, objectMode', testGeneratorOutputPipe, 1, true, true, false, 'duplex'); -test('Can use duplexes with subprocess.stdio[2], objectMode', testGeneratorOutputPipe, 2, false, true, false, 'duplex'); -test('Can use duplexes with subprocess.stderr, objectMode', testGeneratorOutputPipe, 2, true, true, false, 'duplex'); -test('Can use duplexes with subprocess.stdio[*] as output, objectMode', testGeneratorOutputPipe, 3, false, true, false, 'duplex'); -test('Can use duplexes with subprocess.stdio[1], noop transform', testGeneratorOutputPipe, 1, false, false, true, 'duplex'); -test('Can use duplexes with subprocess.stdout, noop transform', testGeneratorOutputPipe, 1, true, false, true, 'duplex'); -test('Can use duplexes with subprocess.stdio[2], noop transform', testGeneratorOutputPipe, 2, false, false, true, 'duplex'); -test('Can use duplexes with subprocess.stderr, noop transform', testGeneratorOutputPipe, 2, true, false, true, 'duplex'); -test('Can use duplexes with subprocess.stdio[*] as output, noop transform', testGeneratorOutputPipe, 3, false, false, true, 'duplex'); -test('Can use duplexes with subprocess.stdio[1], objectMode, noop transform', testGeneratorOutputPipe, 1, false, true, true, 'duplex'); -test('Can use duplexes with subprocess.stdout, objectMode, noop transform', testGeneratorOutputPipe, 1, true, true, true, 'duplex'); -test('Can use duplexes with subprocess.stdio[2], objectMode, noop transform', testGeneratorOutputPipe, 2, false, true, true, 'duplex'); -test('Can use duplexes with subprocess.stderr, objectMode, noop transform', testGeneratorOutputPipe, 2, true, true, true, 'duplex'); -test('Can use duplexes with subprocess.stdio[*] as output, objectMode, noop transform', testGeneratorOutputPipe, 3, false, true, true, 'duplex'); -test('Can use webTransforms with subprocess.stdio[1]', testGeneratorOutputPipe, 1, false, false, false, 'webTransform'); -test('Can use webTransforms with subprocess.stdout', testGeneratorOutputPipe, 1, true, false, false, 'webTransform'); -test('Can use webTransforms with subprocess.stdio[2]', testGeneratorOutputPipe, 2, false, false, false, 'webTransform'); -test('Can use webTransforms with subprocess.stderr', testGeneratorOutputPipe, 2, true, false, false, 'webTransform'); -test('Can use webTransforms with subprocess.stdio[*] as output', testGeneratorOutputPipe, 3, false, false, false, 'webTransform'); -test('Can use webTransforms with subprocess.stdio[1], objectMode', testGeneratorOutputPipe, 1, false, true, false, 'webTransform'); -test('Can use webTransforms with subprocess.stdout, objectMode', testGeneratorOutputPipe, 1, true, true, false, 'webTransform'); -test('Can use webTransforms with subprocess.stdio[2], objectMode', testGeneratorOutputPipe, 2, false, true, false, 'webTransform'); -test('Can use webTransforms with subprocess.stderr, objectMode', testGeneratorOutputPipe, 2, true, true, false, 'webTransform'); -test('Can use webTransforms with subprocess.stdio[*] as output, objectMode', testGeneratorOutputPipe, 3, false, true, false, 'webTransform'); -test('Can use webTransforms with subprocess.stdio[1], noop transform', testGeneratorOutputPipe, 1, false, false, true, 'webTransform'); -test('Can use webTransforms with subprocess.stdout, noop transform', testGeneratorOutputPipe, 1, true, false, true, 'webTransform'); -test('Can use webTransforms with subprocess.stdio[2], noop transform', testGeneratorOutputPipe, 2, false, false, true, 'webTransform'); -test('Can use webTransforms with subprocess.stderr, noop transform', testGeneratorOutputPipe, 2, true, false, true, 'webTransform'); -test('Can use webTransforms with subprocess.stdio[*] as output, noop transform', testGeneratorOutputPipe, 3, false, false, true, 'webTransform'); -test('Can use webTransforms with subprocess.stdio[1], objectMode, noop transform', testGeneratorOutputPipe, 1, false, true, true, 'webTransform'); -test('Can use webTransforms with subprocess.stdout, objectMode, noop transform', testGeneratorOutputPipe, 1, true, true, true, 'webTransform'); -test('Can use webTransforms with subprocess.stdio[2], objectMode, noop transform', testGeneratorOutputPipe, 2, false, true, true, 'webTransform'); -test('Can use webTransforms with subprocess.stderr, objectMode, noop transform', testGeneratorOutputPipe, 2, true, true, true, 'webTransform'); -test('Can use webTransforms with subprocess.stdio[*] as output, objectMode, noop transform', testGeneratorOutputPipe, 3, false, true, true, 'webTransform'); diff --git a/test/transform/generator-return.js b/test/transform/generator-return.js deleted file mode 100644 index f2a75f8b7a..0000000000 --- a/test/transform/generator-return.js +++ /dev/null @@ -1,158 +0,0 @@ -import {Buffer} from 'node:buffer'; -import test from 'ava'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString, foobarUint8Array, foobarBuffer} from '../helpers/input.js'; -import {getOutputGenerator, convertTransformToFinal} from '../helpers/generator.js'; - -setFixtureDirectory(); - -// eslint-disable-next-line max-params -const testGeneratorReturnType = async (t, input, encoding, reject, objectMode, final, execaMethod) => { - const fixtureName = reject ? 'noop-fd.js' : 'noop-fail.js'; - const {stdout} = await execaMethod(fixtureName, ['1', foobarString], { - stdout: convertTransformToFinal(getOutputGenerator(input)(objectMode, true), final), - encoding, - reject, - }); - const typeofChunk = Array.isArray(stdout) ? stdout[0] : stdout; - const output = Buffer.from(typeofChunk, encoding === 'buffer' || objectMode ? undefined : encoding).toString(); - t.is(output, foobarString); -}; - -test('Generator can return string with default encoding', testGeneratorReturnType, foobarString, 'utf8', true, false, false, execa); -test('Generator can return Uint8Array with default encoding', testGeneratorReturnType, foobarUint8Array, 'utf8', true, false, false, execa); -test('Generator can return Buffer with default encoding', testGeneratorReturnType, foobarBuffer, 'utf8', true, false, false, execa); -test('Generator can return string with encoding "utf16le"', testGeneratorReturnType, foobarString, 'utf16le', true, false, false, execa); -test('Generator can return Uint8Array with encoding "utf16le"', testGeneratorReturnType, foobarUint8Array, 'utf16le', true, false, false, execa); -test('Generator can return Buffer with encoding "utf16le"', testGeneratorReturnType, foobarBuffer, 'utf16le', true, false, false, execa); -test('Generator can return string with encoding "buffer"', testGeneratorReturnType, foobarString, 'buffer', true, false, false, execa); -test('Generator can return Uint8Array with encoding "buffer"', testGeneratorReturnType, foobarUint8Array, 'buffer', true, false, false, execa); -test('Generator can return Buffer with encoding "buffer"', testGeneratorReturnType, foobarBuffer, 'buffer', true, false, false, execa); -test('Generator can return string with encoding "hex"', testGeneratorReturnType, foobarString, 'hex', true, false, false, execa); -test('Generator can return Uint8Array with encoding "hex"', testGeneratorReturnType, foobarUint8Array, 'hex', true, false, false, execa); -test('Generator can return Buffer with encoding "hex"', testGeneratorReturnType, foobarBuffer, 'hex', true, false, false, execa); -test('Generator can return string with default encoding, failure', testGeneratorReturnType, foobarString, 'utf8', false, false, false, execa); -test('Generator can return Uint8Array with default encoding, failure', testGeneratorReturnType, foobarUint8Array, 'utf8', false, false, false, execa); -test('Generator can return string with encoding "utf16le", failure', testGeneratorReturnType, foobarString, 'utf16le', false, false, false, execa); -test('Generator can return Uint8Array with encoding "utf16le", failure', testGeneratorReturnType, foobarUint8Array, 'utf16le', false, false, false, execa); -test('Generator can return string with encoding "buffer", failure', testGeneratorReturnType, foobarString, 'buffer', false, false, false, execa); -test('Generator can return Uint8Array with encoding "buffer", failure', testGeneratorReturnType, foobarUint8Array, 'buffer', false, false, false, execa); -test('Generator can return string with encoding "hex", failure', testGeneratorReturnType, foobarString, 'hex', false, false, false, execa); -test('Generator can return Uint8Array with encoding "hex", failure', testGeneratorReturnType, foobarUint8Array, 'hex', false, false, false, execa); -test('Generator can return string with default encoding, objectMode', testGeneratorReturnType, foobarString, 'utf8', true, true, false, execa); -test('Generator can return Uint8Array with default encoding, objectMode', testGeneratorReturnType, foobarUint8Array, 'utf8', true, true, false, execa); -test('Generator can return string with encoding "utf16le", objectMode', testGeneratorReturnType, foobarString, 'utf16le', true, true, false, execa); -test('Generator can return Uint8Array with encoding "utf16le", objectMode', testGeneratorReturnType, foobarUint8Array, 'utf16le', true, true, false, execa); -test('Generator can return string with encoding "buffer", objectMode', testGeneratorReturnType, foobarString, 'buffer', true, true, false, execa); -test('Generator can return Uint8Array with encoding "buffer", objectMode', testGeneratorReturnType, foobarUint8Array, 'buffer', true, true, false, execa); -test('Generator can return string with encoding "hex", objectMode', testGeneratorReturnType, foobarString, 'hex', true, true, false, execa); -test('Generator can return Uint8Array with encoding "hex", objectMode', testGeneratorReturnType, foobarUint8Array, 'hex', true, true, false, execa); -test('Generator can return string with default encoding, objectMode, failure', testGeneratorReturnType, foobarString, 'utf8', false, true, false, execa); -test('Generator can return Uint8Array with default encoding, objectMode, failure', testGeneratorReturnType, foobarUint8Array, 'utf8', false, true, false, execa); -test('Generator can return string with encoding "utf16le", objectMode, failure', testGeneratorReturnType, foobarString, 'utf16le', false, true, false, execa); -test('Generator can return Uint8Array with encoding "utf16le", objectMode, failure', testGeneratorReturnType, foobarUint8Array, 'utf16le', false, true, false, execa); -test('Generator can return string with encoding "buffer", objectMode, failure', testGeneratorReturnType, foobarString, 'buffer', false, true, false, execa); -test('Generator can return Uint8Array with encoding "buffer", objectMode, failure', testGeneratorReturnType, foobarUint8Array, 'buffer', false, true, false, execa); -test('Generator can return string with encoding "hex", objectMode, failure', testGeneratorReturnType, foobarString, 'hex', false, true, false, execa); -test('Generator can return Uint8Array with encoding "hex", objectMode, failure', testGeneratorReturnType, foobarUint8Array, 'hex', false, true, false, execa); -test('Generator can return final string with default encoding', testGeneratorReturnType, foobarString, 'utf8', true, false, true, execa); -test('Generator can return final Uint8Array with default encoding', testGeneratorReturnType, foobarUint8Array, 'utf8', true, false, true, execa); -test('Generator can return final string with encoding "utf16le"', testGeneratorReturnType, foobarString, 'utf16le', true, false, true, execa); -test('Generator can return final Uint8Array with encoding "utf16le"', testGeneratorReturnType, foobarUint8Array, 'utf16le', true, false, true, execa); -test('Generator can return final string with encoding "buffer"', testGeneratorReturnType, foobarString, 'buffer', true, false, true, execa); -test('Generator can return final Uint8Array with encoding "buffer"', testGeneratorReturnType, foobarUint8Array, 'buffer', true, false, true, execa); -test('Generator can return final string with encoding "hex"', testGeneratorReturnType, foobarString, 'hex', true, false, true, execa); -test('Generator can return final Uint8Array with encoding "hex"', testGeneratorReturnType, foobarUint8Array, 'hex', true, false, true, execa); -test('Generator can return final string with default encoding, failure', testGeneratorReturnType, foobarString, 'utf8', false, false, true, execa); -test('Generator can return final Uint8Array with default encoding, failure', testGeneratorReturnType, foobarUint8Array, 'utf8', false, false, true, execa); -test('Generator can return final string with encoding "utf16le", failure', testGeneratorReturnType, foobarString, 'utf16le', false, false, true, execa); -test('Generator can return final Uint8Array with encoding "utf16le", failure', testGeneratorReturnType, foobarUint8Array, 'utf16le', false, false, true, execa); -test('Generator can return final string with encoding "buffer", failure', testGeneratorReturnType, foobarString, 'buffer', false, false, true, execa); -test('Generator can return final Uint8Array with encoding "buffer", failure', testGeneratorReturnType, foobarUint8Array, 'buffer', false, false, true, execa); -test('Generator can return final string with encoding "hex", failure', testGeneratorReturnType, foobarString, 'hex', false, false, true, execa); -test('Generator can return final Uint8Array with encoding "hex", failure', testGeneratorReturnType, foobarUint8Array, 'hex', false, false, true, execa); -test('Generator can return final string with default encoding, objectMode', testGeneratorReturnType, foobarString, 'utf8', true, true, true, execa); -test('Generator can return final Uint8Array with default encoding, objectMode', testGeneratorReturnType, foobarUint8Array, 'utf8', true, true, true, execa); -test('Generator can return final string with encoding "utf16le", objectMode', testGeneratorReturnType, foobarString, 'utf16le', true, true, true, execa); -test('Generator can return final Uint8Array with encoding "utf16le", objectMode', testGeneratorReturnType, foobarUint8Array, 'utf16le', true, true, true, execa); -test('Generator can return final string with encoding "buffer", objectMode', testGeneratorReturnType, foobarString, 'buffer', true, true, true, execa); -test('Generator can return final Uint8Array with encoding "buffer", objectMode', testGeneratorReturnType, foobarUint8Array, 'buffer', true, true, true, execa); -test('Generator can return final string with encoding "hex", objectMode', testGeneratorReturnType, foobarString, 'hex', true, true, true, execa); -test('Generator can return final Uint8Array with encoding "hex", objectMode', testGeneratorReturnType, foobarUint8Array, 'hex', true, true, true, execa); -test('Generator can return final string with default encoding, objectMode, failure', testGeneratorReturnType, foobarString, 'utf8', false, true, true, execa); -test('Generator can return final Uint8Array with default encoding, objectMode, failure', testGeneratorReturnType, foobarUint8Array, 'utf8', false, true, true, execa); -test('Generator can return final string with encoding "utf16le", objectMode, failure', testGeneratorReturnType, foobarString, 'utf16le', false, true, true, execa); -test('Generator can return final Uint8Array with encoding "utf16le", objectMode, failure', testGeneratorReturnType, foobarUint8Array, 'utf16le', false, true, true, execa); -test('Generator can return final string with encoding "buffer", objectMode, failure', testGeneratorReturnType, foobarString, 'buffer', false, true, true, execa); -test('Generator can return final Uint8Array with encoding "buffer", objectMode, failure', testGeneratorReturnType, foobarUint8Array, 'buffer', false, true, true, execa); -test('Generator can return final string with encoding "hex", objectMode, failure', testGeneratorReturnType, foobarString, 'hex', false, true, true, execa); -test('Generator can return final Uint8Array with encoding "hex", objectMode, failure', testGeneratorReturnType, foobarUint8Array, 'hex', false, true, true, execa); -test('Generator can return string with default encoding, sync', testGeneratorReturnType, foobarString, 'utf8', true, false, false, execaSync); -test('Generator can return Uint8Array with default encoding, sync', testGeneratorReturnType, foobarUint8Array, 'utf8', true, false, false, execaSync); -test('Generator can return Buffer with default encoding, sync', testGeneratorReturnType, foobarBuffer, 'utf8', true, false, false, execaSync); -test('Generator can return string with encoding "utf16le", sync', testGeneratorReturnType, foobarString, 'utf16le', true, false, false, execaSync); -test('Generator can return Uint8Array with encoding "utf16le", sync', testGeneratorReturnType, foobarUint8Array, 'utf16le', true, false, false, execaSync); -test('Generator can return Buffer with encoding "utf16le", sync', testGeneratorReturnType, foobarBuffer, 'utf16le', true, false, false, execaSync); -test('Generator can return string with encoding "buffer", sync', testGeneratorReturnType, foobarString, 'buffer', true, false, false, execaSync); -test('Generator can return Uint8Array with encoding "buffer", sync', testGeneratorReturnType, foobarUint8Array, 'buffer', true, false, false, execaSync); -test('Generator can return Buffer with encoding "buffer", sync', testGeneratorReturnType, foobarBuffer, 'buffer', true, false, false, execaSync); -test('Generator can return string with encoding "hex", sync', testGeneratorReturnType, foobarString, 'hex', true, false, false, execaSync); -test('Generator can return Uint8Array with encoding "hex", sync', testGeneratorReturnType, foobarUint8Array, 'hex', true, false, false, execaSync); -test('Generator can return Buffer with encoding "hex", sync', testGeneratorReturnType, foobarBuffer, 'hex', true, false, false, execaSync); -test('Generator can return string with default encoding, failure, sync', testGeneratorReturnType, foobarString, 'utf8', false, false, false, execaSync); -test('Generator can return Uint8Array with default encoding, failure, sync', testGeneratorReturnType, foobarUint8Array, 'utf8', false, false, false, execaSync); -test('Generator can return string with encoding "utf16le", failure, sync', testGeneratorReturnType, foobarString, 'utf16le', false, false, false, execaSync); -test('Generator can return Uint8Array with encoding "utf16le", failure, sync', testGeneratorReturnType, foobarUint8Array, 'utf16le', false, false, false, execaSync); -test('Generator can return string with encoding "buffer", failure, sync', testGeneratorReturnType, foobarString, 'buffer', false, false, false, execaSync); -test('Generator can return Uint8Array with encoding "buffer", failure, sync', testGeneratorReturnType, foobarUint8Array, 'buffer', false, false, false, execaSync); -test('Generator can return string with encoding "hex", failure, sync', testGeneratorReturnType, foobarString, 'hex', false, false, false, execaSync); -test('Generator can return Uint8Array with encoding "hex", failure, sync', testGeneratorReturnType, foobarUint8Array, 'hex', false, false, false, execaSync); -test('Generator can return string with default encoding, objectMode, sync', testGeneratorReturnType, foobarString, 'utf8', true, true, false, execaSync); -test('Generator can return Uint8Array with default encoding, objectMode, sync', testGeneratorReturnType, foobarUint8Array, 'utf8', true, true, false, execaSync); -test('Generator can return string with encoding "utf16le", objectMode, sync', testGeneratorReturnType, foobarString, 'utf16le', true, true, false, execaSync); -test('Generator can return Uint8Array with encoding "utf16le", objectMode, sync', testGeneratorReturnType, foobarUint8Array, 'utf16le', true, true, false, execaSync); -test('Generator can return string with encoding "buffer", objectMode, sync', testGeneratorReturnType, foobarString, 'buffer', true, true, false, execaSync); -test('Generator can return Uint8Array with encoding "buffer", objectMode, sync', testGeneratorReturnType, foobarUint8Array, 'buffer', true, true, false, execaSync); -test('Generator can return string with encoding "hex", objectMode, sync', testGeneratorReturnType, foobarString, 'hex', true, true, false, execaSync); -test('Generator can return Uint8Array with encoding "hex", objectMode, sync', testGeneratorReturnType, foobarUint8Array, 'hex', true, true, false, execaSync); -test('Generator can return string with default encoding, objectMode, failure, sync', testGeneratorReturnType, foobarString, 'utf8', false, true, false, execaSync); -test('Generator can return Uint8Array with default encoding, objectMode, failure, sync', testGeneratorReturnType, foobarUint8Array, 'utf8', false, true, false, execaSync); -test('Generator can return string with encoding "utf16le", objectMode, failure, sync', testGeneratorReturnType, foobarString, 'utf16le', false, true, false, execaSync); -test('Generator can return Uint8Array with encoding "utf16le", objectMode, failure, sync', testGeneratorReturnType, foobarUint8Array, 'utf16le', false, true, false, execaSync); -test('Generator can return string with encoding "buffer", objectMode, failure, sync', testGeneratorReturnType, foobarString, 'buffer', false, true, false, execaSync); -test('Generator can return Uint8Array with encoding "buffer", objectMode, failure, sync', testGeneratorReturnType, foobarUint8Array, 'buffer', false, true, false, execaSync); -test('Generator can return string with encoding "hex", objectMode, failure, sync', testGeneratorReturnType, foobarString, 'hex', false, true, false, execaSync); -test('Generator can return Uint8Array with encoding "hex", objectMode, failure, sync', testGeneratorReturnType, foobarUint8Array, 'hex', false, true, false, execaSync); -test('Generator can return final string with default encoding, sync', testGeneratorReturnType, foobarString, 'utf8', true, false, true, execaSync); -test('Generator can return final Uint8Array with default encoding, sync', testGeneratorReturnType, foobarUint8Array, 'utf8', true, false, true, execaSync); -test('Generator can return final string with encoding "utf16le", sync', testGeneratorReturnType, foobarString, 'utf16le', true, false, true, execaSync); -test('Generator can return final Uint8Array with encoding "utf16le", sync', testGeneratorReturnType, foobarUint8Array, 'utf16le', true, false, true, execaSync); -test('Generator can return final string with encoding "buffer", sync', testGeneratorReturnType, foobarString, 'buffer', true, false, true, execaSync); -test('Generator can return final Uint8Array with encoding "buffer", sync', testGeneratorReturnType, foobarUint8Array, 'buffer', true, false, true, execaSync); -test('Generator can return final string with encoding "hex", sync', testGeneratorReturnType, foobarString, 'hex', true, false, true, execaSync); -test('Generator can return final Uint8Array with encoding "hex", sync', testGeneratorReturnType, foobarUint8Array, 'hex', true, false, true, execaSync); -test('Generator can return final string with default encoding, failure, sync', testGeneratorReturnType, foobarString, 'utf8', false, false, true, execaSync); -test('Generator can return final Uint8Array with default encoding, failure, sync', testGeneratorReturnType, foobarUint8Array, 'utf8', false, false, true, execaSync); -test('Generator can return final string with encoding "utf16le", failure, sync', testGeneratorReturnType, foobarString, 'utf16le', false, false, true, execaSync); -test('Generator can return final Uint8Array with encoding "utf16le", failure, sync', testGeneratorReturnType, foobarUint8Array, 'utf16le', false, false, true, execaSync); -test('Generator can return final string with encoding "buffer", failure, sync', testGeneratorReturnType, foobarString, 'buffer', false, false, true, execaSync); -test('Generator can return final Uint8Array with encoding "buffer", failure, sync', testGeneratorReturnType, foobarUint8Array, 'buffer', false, false, true, execaSync); -test('Generator can return final string with encoding "hex", failure, sync', testGeneratorReturnType, foobarString, 'hex', false, false, true, execaSync); -test('Generator can return final Uint8Array with encoding "hex", failure, sync', testGeneratorReturnType, foobarUint8Array, 'hex', false, false, true, execaSync); -test('Generator can return final string with default encoding, objectMode, sync', testGeneratorReturnType, foobarString, 'utf8', true, true, true, execaSync); -test('Generator can return final Uint8Array with default encoding, objectMode, sync', testGeneratorReturnType, foobarUint8Array, 'utf8', true, true, true, execaSync); -test('Generator can return final string with encoding "utf16le", objectMode, sync', testGeneratorReturnType, foobarString, 'utf16le', true, true, true, execaSync); -test('Generator can return final Uint8Array with encoding "utf16le", objectMode, sync', testGeneratorReturnType, foobarUint8Array, 'utf16le', true, true, true, execaSync); -test('Generator can return final string with encoding "buffer", objectMode, sync', testGeneratorReturnType, foobarString, 'buffer', true, true, true, execaSync); -test('Generator can return final Uint8Array with encoding "buffer", objectMode, sync', testGeneratorReturnType, foobarUint8Array, 'buffer', true, true, true, execaSync); -test('Generator can return final string with encoding "hex", objectMode, sync', testGeneratorReturnType, foobarString, 'hex', true, true, true, execaSync); -test('Generator can return final Uint8Array with encoding "hex", objectMode, sync', testGeneratorReturnType, foobarUint8Array, 'hex', true, true, true, execaSync); -test('Generator can return final string with default encoding, objectMode, failure, sync', testGeneratorReturnType, foobarString, 'utf8', false, true, true, execaSync); -test('Generator can return final Uint8Array with default encoding, objectMode, failure, sync', testGeneratorReturnType, foobarUint8Array, 'utf8', false, true, true, execaSync); -test('Generator can return final string with encoding "utf16le", objectMode, failure, sync', testGeneratorReturnType, foobarString, 'utf16le', false, true, true, execaSync); -test('Generator can return final Uint8Array with encoding "utf16le", objectMode, failure, sync', testGeneratorReturnType, foobarUint8Array, 'utf16le', false, true, true, execaSync); -test('Generator can return final string with encoding "buffer", objectMode, failure, sync', testGeneratorReturnType, foobarString, 'buffer', false, true, true, execaSync); -test('Generator can return final Uint8Array with encoding "buffer", objectMode, failure, sync', testGeneratorReturnType, foobarUint8Array, 'buffer', false, true, true, execaSync); -test('Generator can return final string with encoding "hex", objectMode, failure, sync', testGeneratorReturnType, foobarString, 'hex', false, true, true, execaSync); -test('Generator can return final Uint8Array with encoding "hex", objectMode, failure, sync', testGeneratorReturnType, foobarUint8Array, 'hex', false, true, true, execaSync); diff --git a/test/transform/normalize-transform.js b/test/transform/normalize-transform.js deleted file mode 100644 index 97536dc97b..0000000000 --- a/test/transform/normalize-transform.js +++ /dev/null @@ -1,55 +0,0 @@ -import test from 'ava'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString, foobarUppercase, foobarUint8Array} from '../helpers/input.js'; -import {casedSuffix} from '../helpers/generator.js'; -import {generatorsMap} from '../helpers/map.js'; - -setFixtureDirectory(); - -const testAppendInput = async (t, reversed, type, execaMethod) => { - const stdin = [foobarUint8Array, generatorsMap[type].uppercase(), generatorsMap[type].append()]; - const reversedStdin = reversed ? stdin.reverse() : stdin; - const {stdout} = await execaMethod('stdin-fd.js', ['0'], {stdin: reversedStdin}); - const reversedSuffix = reversed ? casedSuffix.toUpperCase() : casedSuffix; - t.is(stdout, `${foobarUppercase}${reversedSuffix}`); -}; - -test('Can use multiple generators as input', testAppendInput, false, 'generator', execa); -test('Can use multiple generators as input, reversed', testAppendInput, true, 'generator', execa); -test('Can use multiple generators as input, sync', testAppendInput, false, 'generator', execaSync); -test('Can use multiple generators as input, reversed, sync', testAppendInput, true, 'generator', execaSync); -test('Can use multiple duplexes as input', testAppendInput, false, 'duplex', execa); -test('Can use multiple duplexes as input, reversed', testAppendInput, true, 'duplex', execa); -test('Can use multiple webTransforms as input', testAppendInput, false, 'webTransform', execa); -test('Can use multiple webTransforms as input, reversed', testAppendInput, true, 'webTransform', execa); - -const testAppendOutput = async (t, reversed, type, execaMethod) => { - const stdoutOption = [generatorsMap[type].uppercase(), generatorsMap[type].append()]; - const reversedStdoutOption = reversed ? stdoutOption.reverse() : stdoutOption; - const {stdout} = await execaMethod('noop-fd.js', ['1', foobarString], {stdout: reversedStdoutOption}); - const reversedSuffix = reversed ? casedSuffix.toUpperCase() : casedSuffix; - t.is(stdout, `${foobarUppercase}${reversedSuffix}`); -}; - -test('Can use multiple generators as output', testAppendOutput, false, 'generator', execa); -test('Can use multiple generators as output, reversed', testAppendOutput, true, 'generator', execa); -test('Can use multiple generators as output, sync', testAppendOutput, false, 'generator', execaSync); -test('Can use multiple generators as output, reversed, sync', testAppendOutput, true, 'generator', execaSync); -test('Can use multiple duplexes as output', testAppendOutput, false, 'duplex', execa); -test('Can use multiple duplexes as output, reversed', testAppendOutput, true, 'duplex', execa); -test('Can use multiple webTransforms as output', testAppendOutput, false, 'webTransform', execa); -test('Can use multiple webTransforms as output, reversed', testAppendOutput, true, 'webTransform', execa); - -const testGeneratorSyntax = async (t, type, usePlainObject, execaMethod) => { - const transform = generatorsMap[type].uppercase(); - const {stdout} = await execaMethod('noop-fd.js', ['1', foobarString], {stdout: usePlainObject ? transform : transform.transform}); - t.is(stdout, foobarUppercase); -}; - -test('Can pass generators with an options plain object', testGeneratorSyntax, 'generator', false, execa); -test('Can pass generators without an options plain object', testGeneratorSyntax, 'generator', true, execa); -test('Can pass generators with an options plain object, sync', testGeneratorSyntax, 'generator', false, execaSync); -test('Can pass generators without an options plain object, sync', testGeneratorSyntax, 'generator', true, execaSync); -test('Can pass webTransforms with an options plain object', testGeneratorSyntax, 'webTransform', true, execa); -test('Can pass webTransforms without an options plain object', testGeneratorSyntax, 'webTransform', false, execa); diff --git a/test/transform/split-binary.js b/test/transform/split-binary.js deleted file mode 100644 index 31b62983b9..0000000000 --- a/test/transform/split-binary.js +++ /dev/null @@ -1,95 +0,0 @@ -import test from 'ava'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {getOutputsGenerator, resultGenerator} from '../helpers/generator.js'; -import { - simpleFull, - simpleChunks, - simpleFullUint8Array, - simpleFullUtf16Inverted, - simpleFullUtf16Uint8Array, - simpleChunksUint8Array, - simpleFullEnd, - simpleFullEndUtf16Inverted, - simpleFullHex, - simpleLines, - noNewlinesChunks, -} from '../helpers/lines.js'; - -setFixtureDirectory(); - -// eslint-disable-next-line max-params -const testBinaryOption = async (t, binary, input, expectedLines, expectedOutput, objectMode, preserveNewlines, encoding, execaMethod) => { - const lines = []; - const {stdout} = await execaMethod('noop.js', { - stdout: [ - getOutputsGenerator(input)(false, true), - resultGenerator(lines)(objectMode, binary, preserveNewlines), - ], - stripFinalNewline: false, - encoding, - }); - t.deepEqual(lines, expectedLines); - t.deepEqual(stdout, expectedOutput); -}; - -test('Does not split lines when "binary" is true', testBinaryOption, true, simpleChunks, simpleChunksUint8Array, simpleFull, false, true, 'utf8', execa); -test('Splits lines when "binary" is false', testBinaryOption, false, simpleChunks, simpleLines, simpleFull, false, true, 'utf8', execa); -test('Splits lines when "binary" is undefined', testBinaryOption, undefined, simpleChunks, simpleLines, simpleFull, false, true, 'utf8', execa); -test('Does not split lines when "binary" is true, encoding "utf16le"', testBinaryOption, true, simpleChunks, simpleChunksUint8Array, simpleFullUtf16Inverted, false, true, 'utf16le', execa); -test('Splits lines when "binary" is false, encoding "utf16le"', testBinaryOption, false, [simpleFullUtf16Uint8Array], simpleLines, simpleFullUtf16Inverted, false, true, 'utf16le', execa); -test('Splits lines when "binary" is undefined, encoding "utf16le"', testBinaryOption, undefined, [simpleFullUtf16Uint8Array], simpleLines, simpleFullUtf16Inverted, false, true, 'utf16le', execa); -test('Does not split lines when "binary" is true, encoding "buffer"', testBinaryOption, true, simpleChunks, simpleChunksUint8Array, simpleFullUint8Array, false, true, 'buffer', execa); -test('Does not split lines when "binary" is undefined, encoding "buffer"', testBinaryOption, undefined, simpleChunks, simpleChunksUint8Array, simpleFullUint8Array, false, true, 'buffer', execa); -test('Does not split lines when "binary" is false, encoding "buffer"', testBinaryOption, false, simpleChunks, simpleChunksUint8Array, simpleFullUint8Array, false, true, 'buffer', execa); -test('Does not split lines when "binary" is true, encoding "hex"', testBinaryOption, true, simpleChunks, simpleChunksUint8Array, simpleFullHex, false, true, 'hex', execa); -test('Does not split lines when "binary" is undefined, encoding "hex"', testBinaryOption, undefined, simpleChunks, simpleChunksUint8Array, simpleFullHex, false, true, 'hex', execa); -test('Does not split lines when "binary" is false, encoding "hex"', testBinaryOption, false, simpleChunks, simpleChunksUint8Array, simpleFullHex, false, true, 'hex', execa); -test('Does not split lines when "binary" is true, objectMode', testBinaryOption, true, simpleChunks, simpleChunksUint8Array, simpleChunksUint8Array, true, true, 'utf8', execa); -test('Splits lines when "binary" is false, objectMode', testBinaryOption, false, simpleChunks, simpleLines, simpleLines, true, true, 'utf8', execa); -test('Splits lines when "binary" is undefined, objectMode', testBinaryOption, undefined, simpleChunks, simpleLines, simpleLines, true, true, 'utf8', execa); -test('Does not split lines when "binary" is true, preserveNewlines', testBinaryOption, true, simpleChunks, simpleChunksUint8Array, simpleFull, false, false, 'utf8', execa); -test('Splits lines when "binary" is false, preserveNewlines', testBinaryOption, false, simpleChunks, noNewlinesChunks, simpleFullEnd, false, false, 'utf8', execa); -test('Splits lines when "binary" is undefined, preserveNewlines', testBinaryOption, undefined, simpleChunks, noNewlinesChunks, simpleFullEnd, false, false, 'utf8', execa); -test('Does not split lines when "binary" is true, preserveNewlines, encoding "utf16le"', testBinaryOption, true, simpleChunks, simpleChunksUint8Array, simpleFullUtf16Inverted, false, false, 'utf16le', execa); -test('Splits lines when "binary" is false, preserveNewlines, encoding "utf16le"', testBinaryOption, false, [simpleFullUtf16Uint8Array], noNewlinesChunks, simpleFullEndUtf16Inverted, false, false, 'utf16le', execa); -test('Splits lines when "binary" is undefined, preserveNewlines, encoding "utf16le"', testBinaryOption, undefined, [simpleFullUtf16Uint8Array], noNewlinesChunks, simpleFullEndUtf16Inverted, false, false, 'utf16le', execa); -test('Does not split lines when "binary" is true, encoding "buffer", preserveNewlines', testBinaryOption, true, simpleChunks, simpleChunksUint8Array, simpleFullUint8Array, false, false, 'buffer', execa); -test('Does not split lines when "binary" is undefined, encoding "buffer", preserveNewlines', testBinaryOption, undefined, simpleChunks, simpleChunksUint8Array, simpleFullUint8Array, false, false, 'buffer', execa); -test('Does not split lines when "binary" is false, encoding "buffer", preserveNewlines', testBinaryOption, false, simpleChunks, simpleChunksUint8Array, simpleFullUint8Array, false, false, 'buffer', execa); -test('Does not split lines when "binary" is true, objectMode, preserveNewlines', testBinaryOption, true, simpleChunks, simpleChunksUint8Array, simpleChunksUint8Array, true, false, 'utf8', execa); -test('Does not split lines when "binary" is true, encoding "hex", preserveNewlines', testBinaryOption, true, simpleChunks, simpleChunksUint8Array, simpleFullHex, false, false, 'hex', execa); -test('Does not split lines when "binary" is undefined, encoding "hex", preserveNewlines', testBinaryOption, undefined, simpleChunks, simpleChunksUint8Array, simpleFullHex, false, false, 'hex', execa); -test('Does not split lines when "binary" is false, encoding "hex", preserveNewlines', testBinaryOption, false, simpleChunks, simpleChunksUint8Array, simpleFullHex, false, false, 'hex', execa); -test('Splits lines when "binary" is false, objectMode, preserveNewlines', testBinaryOption, false, simpleChunks, noNewlinesChunks, noNewlinesChunks, true, false, 'utf8', execa); -test('Splits lines when "binary" is undefined, objectMode, preserveNewlines', testBinaryOption, undefined, simpleChunks, noNewlinesChunks, noNewlinesChunks, true, false, 'utf8', execa); -test('Does not split lines when "binary" is true, sync', testBinaryOption, true, simpleChunks, simpleChunksUint8Array, simpleFull, false, true, 'utf8', execaSync); -test('Splits lines when "binary" is false, sync', testBinaryOption, false, simpleChunks, simpleLines, simpleFull, false, true, 'utf8', execaSync); -test('Splits lines when "binary" is undefined, sync', testBinaryOption, undefined, simpleChunks, simpleLines, simpleFull, false, true, 'utf8', execaSync); -test('Does not split lines when "binary" is true, encoding "utf16le", sync', testBinaryOption, true, simpleChunks, simpleChunksUint8Array, simpleFullUtf16Inverted, false, true, 'utf16le', execaSync); -test('Splits lines when "binary" is false, encoding "utf16le", sync', testBinaryOption, false, [simpleFullUtf16Uint8Array], simpleLines, simpleFullUtf16Inverted, false, true, 'utf16le', execaSync); -test('Splits lines when "binary" is undefined, encoding "utf16le", sync', testBinaryOption, undefined, [simpleFullUtf16Uint8Array], simpleLines, simpleFullUtf16Inverted, false, true, 'utf16le', execaSync); -test('Does not split lines when "binary" is true, encoding "buffer", sync', testBinaryOption, true, simpleChunks, simpleChunksUint8Array, simpleFullUint8Array, false, true, 'buffer', execaSync); -test('Does not split lines when "binary" is undefined, encoding "buffer", sync', testBinaryOption, undefined, simpleChunks, simpleChunksUint8Array, simpleFullUint8Array, false, true, 'buffer', execaSync); -test('Does not split lines when "binary" is false, encoding "buffer", sync', testBinaryOption, false, simpleChunks, simpleChunksUint8Array, simpleFullUint8Array, false, true, 'buffer', execaSync); -test('Does not split lines when "binary" is true, encoding "hex", sync', testBinaryOption, true, simpleChunks, simpleChunksUint8Array, simpleFullHex, false, true, 'hex', execaSync); -test('Does not split lines when "binary" is undefined, encoding "hex", sync', testBinaryOption, undefined, simpleChunks, simpleChunksUint8Array, simpleFullHex, false, true, 'hex', execaSync); -test('Does not split lines when "binary" is false, encoding "hex", sync', testBinaryOption, false, simpleChunks, simpleChunksUint8Array, simpleFullHex, false, true, 'hex', execaSync); -test('Does not split lines when "binary" is true, objectMode, sync', testBinaryOption, true, simpleChunks, simpleChunksUint8Array, simpleChunksUint8Array, true, true, 'utf8', execaSync); -test('Splits lines when "binary" is false, objectMode, sync', testBinaryOption, false, simpleChunks, simpleLines, simpleLines, true, true, 'utf8', execaSync); -test('Splits lines when "binary" is undefined, objectMode, sync', testBinaryOption, undefined, simpleChunks, simpleLines, simpleLines, true, true, 'utf8', execaSync); -test('Does not split lines when "binary" is true, preserveNewlines, sync', testBinaryOption, true, simpleChunks, simpleChunksUint8Array, simpleFull, false, false, 'utf8', execaSync); -test('Splits lines when "binary" is false, preserveNewlines, sync', testBinaryOption, false, simpleChunks, noNewlinesChunks, simpleFullEnd, false, false, 'utf8', execaSync); -test('Splits lines when "binary" is undefined, preserveNewlines, sync', testBinaryOption, undefined, simpleChunks, noNewlinesChunks, simpleFullEnd, false, false, 'utf8', execaSync); -test('Does not split lines when "binary" is true, preserveNewlines, encoding "utf16le", sync', testBinaryOption, true, simpleChunks, simpleChunksUint8Array, simpleFullUtf16Inverted, false, false, 'utf16le', execaSync); -test('Splits lines when "binary" is false, preserveNewlines, encoding "utf16le", sync', testBinaryOption, false, [simpleFullUtf16Uint8Array], noNewlinesChunks, simpleFullEndUtf16Inverted, false, false, 'utf16le', execaSync); -test('Splits lines when "binary" is undefined, preserveNewlines, encoding "utf16le", sync', testBinaryOption, undefined, [simpleFullUtf16Uint8Array], noNewlinesChunks, simpleFullEndUtf16Inverted, false, false, 'utf16le', execaSync); -test('Does not split lines when "binary" is true, encoding "buffer", preserveNewlines, sync', testBinaryOption, true, simpleChunks, simpleChunksUint8Array, simpleFullUint8Array, false, false, 'buffer', execaSync); -test('Does not split lines when "binary" is undefined, encoding "buffer", preserveNewlines, sync', testBinaryOption, undefined, simpleChunks, simpleChunksUint8Array, simpleFullUint8Array, false, false, 'buffer', execaSync); -test('Does not split lines when "binary" is false, encoding "buffer", preserveNewlines, sync', testBinaryOption, false, simpleChunks, simpleChunksUint8Array, simpleFullUint8Array, false, false, 'buffer', execaSync); -test('Does not split lines when "binary" is true, objectMode, preserveNewlines, sync', testBinaryOption, true, simpleChunks, simpleChunksUint8Array, simpleChunksUint8Array, true, false, 'utf8', execaSync); -test('Does not split lines when "binary" is true, encoding "hex", preserveNewlines, sync', testBinaryOption, true, simpleChunks, simpleChunksUint8Array, simpleFullHex, false, false, 'hex', execaSync); -test('Does not split lines when "binary" is undefined, encoding "hex", preserveNewlines, sync', testBinaryOption, undefined, simpleChunks, simpleChunksUint8Array, simpleFullHex, false, false, 'hex', execaSync); -test('Does not split lines when "binary" is false, encoding "hex", preserveNewlines, sync', testBinaryOption, false, simpleChunks, simpleChunksUint8Array, simpleFullHex, false, false, 'hex', execaSync); -test('Splits lines when "binary" is false, objectMode, preserveNewlines, sync', testBinaryOption, false, simpleChunks, noNewlinesChunks, noNewlinesChunks, true, false, 'utf8', execaSync); -test('Splits lines when "binary" is undefined, objectMode, preserveNewlines, sync', testBinaryOption, undefined, simpleChunks, noNewlinesChunks, noNewlinesChunks, true, false, 'utf8', execaSync); diff --git a/test/transform/split-lines.js b/test/transform/split-lines.js deleted file mode 100644 index 05f633369e..0000000000 --- a/test/transform/split-lines.js +++ /dev/null @@ -1,174 +0,0 @@ -import test from 'ava'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {getStdio} from '../helpers/stdio.js'; -import {getOutputsGenerator, resultGenerator} from '../helpers/generator.js'; -import { - singleFull, - singleFullEnd, - simpleFull, - simpleChunks, - simpleFullEnd, - simpleFullEndChunks, - simpleLines, - simpleFullEndLines, - noNewlinesFull, - noNewlinesChunks, -} from '../helpers/lines.js'; - -setFixtureDirectory(); - -const windowsFull = 'aaa\r\nbbb\r\nccc'; -const windowsFullEnd = `${windowsFull}\r\n`; -const windowsChunks = [windowsFull]; -const windowsLines = ['aaa\r\n', 'bbb\r\n', 'ccc']; -const noNewlinesFullEnd = `${noNewlinesFull}\n`; -const noNewlinesLines = ['aaabbbccc']; -const singleChunks = [singleFull]; -const noLines = []; -const emptyFull = ''; -const emptyChunks = [emptyFull]; -const manyEmptyChunks = [emptyFull, emptyFull, emptyFull]; -const newlineFull = '\n'; -const newlineChunks = [newlineFull]; -const newlinesFull = '\n\n\n'; -const newlinesChunks = [newlinesFull]; -const newlinesLines = ['\n', '\n', '\n']; -const windowsNewlinesFull = '\r\n\r\n\r\n'; -const windowsNewlinesChunks = [windowsNewlinesFull]; -const windowsNewlinesLines = ['\r\n', '\r\n', '\r\n']; -const runOverChunks = ['aaa\nb', 'b', 'b\nccc']; -const bigFull = '.'.repeat(1e5); -const bigFullEnd = `${bigFull}\n`; -const bigChunks = [bigFull]; -const manyChunks = Array.from({length: 1e3}).fill('.'); -const manyFull = manyChunks.join(''); -const manyFullEnd = `${manyFull}\n`; -const manyLines = [manyFull]; - -// eslint-disable-next-line max-params -const testLines = async (t, fdNumber, input, expectedLines, expectedOutput, objectMode, preserveNewlines, execaMethod) => { - const lines = []; - const {stdio} = await execaMethod('noop-fd.js', [`${fdNumber}`], { - ...getStdio(fdNumber, [ - getOutputsGenerator(input)(false, true), - resultGenerator(lines)(objectMode, false, preserveNewlines), - ]), - stripFinalNewline: false, - }); - t.deepEqual(lines, expectedLines); - t.deepEqual(stdio[fdNumber], expectedOutput); -}; - -test('Split stdout - n newlines, 1 chunk', testLines, 1, simpleChunks, simpleLines, simpleFull, false, true, execa); -test('Split stderr - n newlines, 1 chunk', testLines, 2, simpleChunks, simpleLines, simpleFull, false, true, execa); -test('Split stdio[*] - n newlines, 1 chunk', testLines, 3, simpleChunks, simpleLines, simpleFull, false, true, execa); -test('Split stdout - preserveNewlines, n chunks', testLines, 1, noNewlinesChunks, noNewlinesLines, noNewlinesFull, false, true, execa); -test('Split stdout - 0 newlines, 1 chunk', testLines, 1, singleChunks, singleChunks, singleFull, false, true, execa); -test('Split stdout - empty, 1 chunk', testLines, 1, emptyChunks, noLines, emptyFull, false, true, execa); -test('Split stdout - Windows newlines', testLines, 1, windowsChunks, windowsLines, windowsFull, false, true, execa); -test('Split stdout - chunk ends with newline', testLines, 1, simpleFullEndChunks, simpleFullEndLines, simpleFullEnd, false, true, execa); -test('Split stdout - single newline', testLines, 1, newlineChunks, newlineChunks, newlineFull, false, true, execa); -test('Split stdout - only newlines', testLines, 1, newlinesChunks, newlinesLines, newlinesFull, false, true, execa); -test('Split stdout - only Windows newlines', testLines, 1, windowsNewlinesChunks, windowsNewlinesLines, windowsNewlinesFull, false, true, execa); -test('Split stdout - line split over multiple chunks', testLines, 1, runOverChunks, simpleLines, simpleFull, false, true, execa); -test('Split stdout - 0 newlines, big line', testLines, 1, bigChunks, bigChunks, bigFull, false, true, execa); -test('Split stdout - 0 newlines, many chunks', testLines, 1, manyChunks, manyLines, manyFull, false, true, execa); -test('Split stdout - n newlines, 1 chunk, objectMode', testLines, 1, simpleChunks, simpleLines, simpleLines, true, true, execa); -test('Split stderr - n newlines, 1 chunk, objectMode', testLines, 2, simpleChunks, simpleLines, simpleLines, true, true, execa); -test('Split stdio[*] - n newlines, 1 chunk, objectMode', testLines, 3, simpleChunks, simpleLines, simpleLines, true, true, execa); -test('Split stdout - preserveNewlines, n chunks, objectMode', testLines, 1, noNewlinesChunks, noNewlinesLines, noNewlinesLines, true, true, execa); -test('Split stdout - empty, 1 chunk, objectMode', testLines, 1, emptyChunks, noLines, noLines, true, true, execa); -test('Split stdout - 0 newlines, 1 chunk, objectMode', testLines, 1, singleChunks, singleChunks, singleChunks, true, true, execa); -test('Split stdout - Windows newlines, objectMode', testLines, 1, windowsChunks, windowsLines, windowsLines, true, true, execa); -test('Split stdout - chunk ends with newline, objectMode', testLines, 1, simpleFullEndChunks, simpleFullEndLines, simpleFullEndLines, true, true, execa); -test('Split stdout - single newline, objectMode', testLines, 1, newlineChunks, newlineChunks, newlineChunks, true, true, execa); -test('Split stdout - only newlines, objectMode', testLines, 1, newlinesChunks, newlinesLines, newlinesLines, true, true, execa); -test('Split stdout - only Windows newlines, objectMode', testLines, 1, windowsNewlinesChunks, windowsNewlinesLines, windowsNewlinesLines, true, true, execa); -test('Split stdout - line split over multiple chunks, objectMode', testLines, 1, runOverChunks, simpleLines, simpleLines, true, true, execa); -test('Split stdout - 0 newlines, big line, objectMode', testLines, 1, bigChunks, bigChunks, bigChunks, true, true, execa); -test('Split stdout - 0 newlines, many chunks, objectMode', testLines, 1, manyChunks, manyLines, manyLines, true, true, execa); -test('Split stdout - n newlines, 1 chunk, preserveNewlines', testLines, 1, simpleChunks, noNewlinesChunks, simpleFullEnd, false, false, execa); -test('Split stderr - n newlines, 1 chunk, preserveNewlines', testLines, 2, simpleChunks, noNewlinesChunks, simpleFullEnd, false, false, execa); -test('Split stdio[*] - n newlines, 1 chunk, preserveNewlines', testLines, 3, simpleChunks, noNewlinesChunks, simpleFullEnd, false, false, execa); -test('Split stdout - preserveNewlines, n chunks, preserveNewlines', testLines, 1, noNewlinesChunks, noNewlinesLines, noNewlinesFullEnd, false, false, execa); -test('Split stdout - empty, 1 chunk, preserveNewlines', testLines, 1, emptyChunks, noLines, emptyFull, false, false, execa); -test('Split stdout - 0 newlines, 1 chunk, preserveNewlines', testLines, 1, singleChunks, singleChunks, singleFullEnd, false, false, execa); -test('Split stdout - Windows newlines, preserveNewlines', testLines, 1, windowsChunks, noNewlinesChunks, windowsFullEnd, false, false, execa); -test('Split stdout - chunk ends with newline, preserveNewlines', testLines, 1, simpleFullEndChunks, noNewlinesChunks, simpleFullEnd, false, false, execa); -test('Split stdout - single newline, preserveNewlines', testLines, 1, newlineChunks, emptyChunks, newlineFull, false, false, execa); -test('Split stdout - only newlines, preserveNewlines', testLines, 1, newlinesChunks, manyEmptyChunks, newlinesFull, false, false, execa); -test('Split stdout - only Windows newlines, preserveNewlines', testLines, 1, windowsNewlinesChunks, manyEmptyChunks, windowsNewlinesFull, false, false, execa); -test('Split stdout - line split over multiple chunks, preserveNewlines', testLines, 1, runOverChunks, noNewlinesChunks, simpleFullEnd, false, false, execa); -test('Split stdout - 0 newlines, big line, preserveNewlines', testLines, 1, bigChunks, bigChunks, bigFullEnd, false, false, execa); -test('Split stdout - 0 newlines, many chunks, preserveNewlines', testLines, 1, manyChunks, manyLines, manyFullEnd, false, false, execa); -test('Split stdout - n newlines, 1 chunk, objectMode, preserveNewlines', testLines, 1, simpleChunks, noNewlinesChunks, noNewlinesChunks, true, false, execa); -test('Split stderr - n newlines, 1 chunk, objectMode, preserveNewlines', testLines, 2, simpleChunks, noNewlinesChunks, noNewlinesChunks, true, false, execa); -test('Split stdio[*] - n newlines, 1 chunk, objectMode, preserveNewlines', testLines, 3, simpleChunks, noNewlinesChunks, noNewlinesChunks, true, false, execa); -test('Split stdout - preserveNewlines, n chunks, objectMode, preserveNewlines', testLines, 1, noNewlinesChunks, noNewlinesLines, noNewlinesLines, true, false, execa); -test('Split stdout - empty, 1 chunk, objectMode, preserveNewlines', testLines, 1, emptyChunks, noLines, noLines, true, false, execa); -test('Split stdout - 0 newlines, 1 chunk, objectMode, preserveNewlines', testLines, 1, singleChunks, singleChunks, singleChunks, true, false, execa); -test('Split stdout - Windows newlines, objectMode, preserveNewlines', testLines, 1, windowsChunks, noNewlinesChunks, noNewlinesChunks, true, false, execa); -test('Split stdout - chunk ends with newline, objectMode, preserveNewlines', testLines, 1, simpleFullEndChunks, noNewlinesChunks, noNewlinesChunks, true, false, execa); -test('Split stdout - single newline, objectMode, preserveNewlines', testLines, 1, newlineChunks, emptyChunks, emptyChunks, true, false, execa); -test('Split stdout - only newlines, objectMode, preserveNewlines', testLines, 1, newlinesChunks, manyEmptyChunks, manyEmptyChunks, true, false, execa); -test('Split stdout - only Windows newlines, objectMode, preserveNewlines', testLines, 1, windowsNewlinesChunks, manyEmptyChunks, manyEmptyChunks, true, false, execa); -test('Split stdout - line split over multiple chunks, objectMode, preserveNewlines', testLines, 1, runOverChunks, noNewlinesChunks, noNewlinesChunks, true, false, execa); -test('Split stdout - 0 newlines, big line, objectMode, preserveNewlines', testLines, 1, bigChunks, bigChunks, bigChunks, true, false, execa); -test('Split stdout - 0 newlines, many chunks, objectMode, preserveNewlines', testLines, 1, manyChunks, manyLines, manyLines, true, false, execa); -test('Split stdout - n newlines, 1 chunk, sync', testLines, 1, simpleChunks, simpleLines, simpleFull, false, true, execaSync); -test('Split stderr - n newlines, 1 chunk, sync', testLines, 2, simpleChunks, simpleLines, simpleFull, false, true, execaSync); -test('Split stdio[*] - n newlines, 1 chunk, sync', testLines, 3, simpleChunks, simpleLines, simpleFull, false, true, execaSync); -test('Split stdout - preserveNewlines, n chunks, sync', testLines, 1, noNewlinesChunks, noNewlinesLines, noNewlinesFull, false, true, execaSync); -test('Split stdout - 0 newlines, 1 chunk, sync', testLines, 1, singleChunks, singleChunks, singleFull, false, true, execaSync); -test('Split stdout - empty, 1 chunk, sync', testLines, 1, emptyChunks, noLines, emptyFull, false, true, execaSync); -test('Split stdout - Windows newlines, sync', testLines, 1, windowsChunks, windowsLines, windowsFull, false, true, execaSync); -test('Split stdout - chunk ends with newline, sync', testLines, 1, simpleFullEndChunks, simpleFullEndLines, simpleFullEnd, false, true, execaSync); -test('Split stdout - single newline, sync', testLines, 1, newlineChunks, newlineChunks, newlineFull, false, true, execaSync); -test('Split stdout - only newlines, sync', testLines, 1, newlinesChunks, newlinesLines, newlinesFull, false, true, execaSync); -test('Split stdout - only Windows newlines, sync', testLines, 1, windowsNewlinesChunks, windowsNewlinesLines, windowsNewlinesFull, false, true, execaSync); -test('Split stdout - line split over multiple chunks, sync', testLines, 1, runOverChunks, simpleLines, simpleFull, false, true, execaSync); -test('Split stdout - 0 newlines, big line, sync', testLines, 1, bigChunks, bigChunks, bigFull, false, true, execaSync); -test('Split stdout - 0 newlines, many chunks, sync', testLines, 1, manyChunks, manyLines, manyFull, false, true, execaSync); -test('Split stdout - n newlines, 1 chunk, objectMode, sync', testLines, 1, simpleChunks, simpleLines, simpleLines, true, true, execaSync); -test('Split stderr - n newlines, 1 chunk, objectMode, sync', testLines, 2, simpleChunks, simpleLines, simpleLines, true, true, execaSync); -test('Split stdio[*] - n newlines, 1 chunk, objectMode, sync', testLines, 3, simpleChunks, simpleLines, simpleLines, true, true, execaSync); -test('Split stdout - preserveNewlines, n chunks, objectMode, sync', testLines, 1, noNewlinesChunks, noNewlinesLines, noNewlinesLines, true, true, execaSync); -test('Split stdout - empty, 1 chunk, objectMode, sync', testLines, 1, emptyChunks, noLines, noLines, true, true, execaSync); -test('Split stdout - 0 newlines, 1 chunk, objectMode, sync', testLines, 1, singleChunks, singleChunks, singleChunks, true, true, execaSync); -test('Split stdout - Windows newlines, objectMode, sync', testLines, 1, windowsChunks, windowsLines, windowsLines, true, true, execaSync); -test('Split stdout - chunk ends with newline, objectMode, sync', testLines, 1, simpleFullEndChunks, simpleFullEndLines, simpleFullEndLines, true, true, execaSync); -test('Split stdout - single newline, objectMode, sync', testLines, 1, newlineChunks, newlineChunks, newlineChunks, true, true, execaSync); -test('Split stdout - only newlines, objectMode, sync', testLines, 1, newlinesChunks, newlinesLines, newlinesLines, true, true, execaSync); -test('Split stdout - only Windows newlines, objectMode, sync', testLines, 1, windowsNewlinesChunks, windowsNewlinesLines, windowsNewlinesLines, true, true, execaSync); -test('Split stdout - line split over multiple chunks, objectMode, sync', testLines, 1, runOverChunks, simpleLines, simpleLines, true, true, execaSync); -test('Split stdout - 0 newlines, big line, objectMode, sync', testLines, 1, bigChunks, bigChunks, bigChunks, true, true, execaSync); -test('Split stdout - 0 newlines, many chunks, objectMode, sync', testLines, 1, manyChunks, manyLines, manyLines, true, true, execaSync); -test('Split stdout - n newlines, 1 chunk, preserveNewlines, sync', testLines, 1, simpleChunks, noNewlinesChunks, simpleFullEnd, false, false, execaSync); -test('Split stderr - n newlines, 1 chunk, preserveNewlines, sync', testLines, 2, simpleChunks, noNewlinesChunks, simpleFullEnd, false, false, execaSync); -test('Split stdio[*] - n newlines, 1 chunk, preserveNewlines, sync', testLines, 3, simpleChunks, noNewlinesChunks, simpleFullEnd, false, false, execaSync); -test('Split stdout - preserveNewlines, n chunks, preserveNewlines, sync', testLines, 1, noNewlinesChunks, noNewlinesLines, noNewlinesFullEnd, false, false, execaSync); -test('Split stdout - empty, 1 chunk, preserveNewlines, sync', testLines, 1, emptyChunks, noLines, emptyFull, false, false, execaSync); -test('Split stdout - 0 newlines, 1 chunk, preserveNewlines, sync', testLines, 1, singleChunks, singleChunks, singleFullEnd, false, false, execaSync); -test('Split stdout - Windows newlines, preserveNewlines, sync', testLines, 1, windowsChunks, noNewlinesChunks, windowsFullEnd, false, false, execaSync); -test('Split stdout - chunk ends with newline, preserveNewlines, sync', testLines, 1, simpleFullEndChunks, noNewlinesChunks, simpleFullEnd, false, false, execaSync); -test('Split stdout - single newline, preserveNewlines, sync', testLines, 1, newlineChunks, emptyChunks, newlineFull, false, false, execaSync); -test('Split stdout - only newlines, preserveNewlines, sync', testLines, 1, newlinesChunks, manyEmptyChunks, newlinesFull, false, false, execaSync); -test('Split stdout - only Windows newlines, preserveNewlines, sync', testLines, 1, windowsNewlinesChunks, manyEmptyChunks, windowsNewlinesFull, false, false, execaSync); -test('Split stdout - line split over multiple chunks, preserveNewlines, sync', testLines, 1, runOverChunks, noNewlinesChunks, simpleFullEnd, false, false, execaSync); -test('Split stdout - 0 newlines, big line, preserveNewlines, sync', testLines, 1, bigChunks, bigChunks, bigFullEnd, false, false, execaSync); -test('Split stdout - 0 newlines, many chunks, preserveNewlines, sync', testLines, 1, manyChunks, manyLines, manyFullEnd, false, false, execaSync); -test('Split stdout - n newlines, 1 chunk, objectMode, preserveNewlines, sync', testLines, 1, simpleChunks, noNewlinesChunks, noNewlinesChunks, true, false, execaSync); -test('Split stderr - n newlines, 1 chunk, objectMode, preserveNewlines, sync', testLines, 2, simpleChunks, noNewlinesChunks, noNewlinesChunks, true, false, execaSync); -test('Split stdio[*] - n newlines, 1 chunk, objectMode, preserveNewlines, sync', testLines, 3, simpleChunks, noNewlinesChunks, noNewlinesChunks, true, false, execaSync); -test('Split stdout - preserveNewlines, n chunks, objectMode, preserveNewlines, sync', testLines, 1, noNewlinesChunks, noNewlinesLines, noNewlinesLines, true, false, execaSync); -test('Split stdout - empty, 1 chunk, objectMode, preserveNewlines, sync', testLines, 1, emptyChunks, noLines, noLines, true, false, execaSync); -test('Split stdout - 0 newlines, 1 chunk, objectMode, preserveNewlines, sync', testLines, 1, singleChunks, singleChunks, singleChunks, true, false, execaSync); -test('Split stdout - Windows newlines, objectMode, preserveNewlines, sync', testLines, 1, windowsChunks, noNewlinesChunks, noNewlinesChunks, true, false, execaSync); -test('Split stdout - chunk ends with newline, objectMode, preserveNewlines, sync', testLines, 1, simpleFullEndChunks, noNewlinesChunks, noNewlinesChunks, true, false, execaSync); -test('Split stdout - single newline, objectMode, preserveNewlines, sync', testLines, 1, newlineChunks, emptyChunks, emptyChunks, true, false, execaSync); -test('Split stdout - only newlines, objectMode, preserveNewlines, sync', testLines, 1, newlinesChunks, manyEmptyChunks, manyEmptyChunks, true, false, execaSync); -test('Split stdout - only Windows newlines, objectMode, preserveNewlines, sync', testLines, 1, windowsNewlinesChunks, manyEmptyChunks, manyEmptyChunks, true, false, execaSync); -test('Split stdout - line split over multiple chunks, objectMode, preserveNewlines, sync', testLines, 1, runOverChunks, noNewlinesChunks, noNewlinesChunks, true, false, execaSync); -test('Split stdout - 0 newlines, big line, objectMode, preserveNewlines, sync', testLines, 1, bigChunks, bigChunks, bigChunks, true, false, execaSync); -test('Split stdout - 0 newlines, many chunks, objectMode, preserveNewlines, sync', testLines, 1, manyChunks, manyLines, manyLines, true, false, execaSync); diff --git a/test/transform/split-newline.js b/test/transform/split-newline.js deleted file mode 100644 index 26f8bf979e..0000000000 --- a/test/transform/split-newline.js +++ /dev/null @@ -1,37 +0,0 @@ -import test from 'ava'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {getOutputsGenerator, noopGenerator, noopAsyncGenerator} from '../helpers/generator.js'; -import {singleFull, singleFullEnd} from '../helpers/lines.js'; - -setFixtureDirectory(); - -const singleFullEndWindows = `${singleFull}\r\n`; -const mixedNewlines = '.\n.\r\n.\n.\r\n.\n'; - -const testStripNewline = async (t, input, expectedOutput, execaMethod) => { - const {stdout} = await execaMethod('noop.js', { - stdout: getOutputsGenerator([input])(), - stripFinalNewline: false, - }); - t.is(stdout, expectedOutput); -}; - -test('Strips newline when user do not mistakenly yield one at the end', testStripNewline, singleFull, singleFullEnd, execa); -test('Strips newline when user mistakenly yielded one at the end', testStripNewline, singleFullEnd, singleFullEnd, execa); -test('Strips newline when user mistakenly yielded one at the end, Windows newline', testStripNewline, singleFullEndWindows, singleFullEndWindows, execa); -test('Strips newline when user do not mistakenly yield one at the end, sync', testStripNewline, singleFull, singleFullEnd, execaSync); -test('Strips newline when user mistakenly yielded one at the end, sync', testStripNewline, singleFullEnd, singleFullEnd, execaSync); -test('Strips newline when user mistakenly yielded one at the end, Windows newline, sync', testStripNewline, singleFullEndWindows, singleFullEndWindows, execaSync); - -const testMixNewlines = async (t, generator, execaMethod) => { - const {stdout} = await execaMethod('noop-fd.js', ['1', mixedNewlines], { - stdout: generator(), - stripFinalNewline: false, - }); - t.is(stdout, mixedNewlines); -}; - -test('Can mix Unix and Windows newlines', testMixNewlines, noopGenerator, execa); -test('Can mix Unix and Windows newlines, sync', testMixNewlines, noopGenerator, execaSync); -test('Can mix Unix and Windows newlines, async', testMixNewlines, noopAsyncGenerator, execa); diff --git a/test/transform/split-transform.js b/test/transform/split-transform.js deleted file mode 100644 index 4167f9b2c2..0000000000 --- a/test/transform/split-transform.js +++ /dev/null @@ -1,83 +0,0 @@ -import test from 'ava'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {getOutputsGenerator, resultGenerator} from '../helpers/generator.js'; -import { - foobarString, - foobarUint8Array, - foobarObject, - foobarObjectString, -} from '../helpers/input.js'; - -setFixtureDirectory(); - -const resultUint8ArrayGenerator = function * (lines, chunk) { - lines.push(chunk); - yield new TextEncoder().encode(chunk); -}; - -// eslint-disable-next-line max-params -const testStringToUint8Array = async (t, expectedOutput, objectMode, preserveNewlines, execaMethod) => { - const lines = []; - const {stdout} = await execaMethod('noop-fd.js', ['1', foobarString], { - stdout: { - transform: resultUint8ArrayGenerator.bind(undefined, lines), - objectMode, - preserveNewlines, - }, - lines: true, - }); - t.deepEqual(lines, [foobarString]); - t.deepEqual(stdout, expectedOutput); -}; - -test('Line splitting when converting from string to Uint8Array', testStringToUint8Array, [foobarString], false, true, execa); -test('Line splitting when converting from string to Uint8Array, objectMode', testStringToUint8Array, [foobarUint8Array], true, true, execa); -test('Line splitting when converting from string to Uint8Array, preserveNewlines', testStringToUint8Array, [foobarString], false, false, execa); -test('Line splitting when converting from string to Uint8Array, objectMode, preserveNewlines', testStringToUint8Array, [foobarUint8Array], true, false, execa); -test('Line splitting when converting from string to Uint8Array, sync', testStringToUint8Array, [foobarString], false, true, execaSync); -test('Line splitting when converting from string to Uint8Array, objectMode, sync', testStringToUint8Array, [foobarUint8Array], true, true, execaSync); -test('Line splitting when converting from string to Uint8Array, preserveNewlines, sync', testStringToUint8Array, [foobarString], false, false, execaSync); -test('Line splitting when converting from string to Uint8Array, objectMode, preserveNewlines, sync', testStringToUint8Array, [foobarUint8Array], true, false, execaSync); - -const serializeResultGenerator = function * (lines, chunk) { - lines.push(chunk); - yield JSON.stringify(chunk); -}; - -const testUnsetObjectMode = async (t, expectedOutput, preserveNewlines, execaMethod) => { - const lines = []; - const {stdout} = await execaMethod('noop.js', { - stdout: [ - getOutputsGenerator([foobarObject])(true), - {transform: serializeResultGenerator.bind(undefined, lines), preserveNewlines, objectMode: false}, - ], - stripFinalNewline: false, - }); - t.deepEqual(lines, [foobarObject]); - t.is(stdout, expectedOutput); -}; - -test('Can switch from objectMode to non-objectMode', testUnsetObjectMode, `${foobarObjectString}\n`, false, execa); -test('Can switch from objectMode to non-objectMode, preserveNewlines', testUnsetObjectMode, foobarObjectString, true, execa); -test('Can switch from objectMode to non-objectMode, sync', testUnsetObjectMode, `${foobarObjectString}\n`, false, execaSync); -test('Can switch from objectMode to non-objectMode, preserveNewlines, sync', testUnsetObjectMode, foobarObjectString, true, execaSync); - -// eslint-disable-next-line max-params -const testYieldArray = async (t, input, expectedLines, expectedOutput, execaMethod) => { - const lines = []; - const {stdout} = await execaMethod('noop.js', { - stdout: [ - getOutputsGenerator(input)(), - resultGenerator(lines)(), - ], - stripFinalNewline: false, - }); - t.deepEqual(lines, expectedLines); - t.deepEqual(stdout, expectedOutput); -}; - -test('Can use "yield* array" to produce multiple lines', testYieldArray, [foobarString, foobarString], [foobarString, foobarString], `${foobarString}\n${foobarString}\n`, execa); -test('Can use "yield* array" to produce empty lines', testYieldArray, [foobarString, ''], [foobarString, ''], `${foobarString}\n\n`, execa); -test('Can use "yield* array" to produce multiple lines, sync', testYieldArray, [foobarString, foobarString], [foobarString, foobarString], `${foobarString}\n${foobarString}\n`, execaSync); -test('Can use "yield* array" to produce empty lines, sync', testYieldArray, [foobarString, ''], [foobarString, ''], `${foobarString}\n\n`, execaSync); diff --git a/test/transform/validate.js b/test/transform/validate.js deleted file mode 100644 index 74586e7e41..0000000000 --- a/test/transform/validate.js +++ /dev/null @@ -1,74 +0,0 @@ -import test from 'ava'; -import {execa, execaSync} from '../../index.js'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {getStdio} from '../helpers/stdio.js'; -import {foobarUint8Array, foobarObject} from '../helpers/input.js'; -import {serializeGenerator, getOutputGenerator, convertTransformToFinal} from '../helpers/generator.js'; - -setFixtureDirectory(); - -const getMessage = input => input === null || input === undefined - ? 'not be called at all' - : 'a string or an Uint8Array'; - -const lastInputGenerator = input => objectMode => [foobarUint8Array, getOutputGenerator(input)(objectMode)]; -const inputGenerator = input => objectMode => [...lastInputGenerator(input)(objectMode), serializeGenerator(true)]; - -// eslint-disable-next-line max-params -const testGeneratorReturn = async (t, fdNumber, generator, input, objectMode, isInput) => { - const fixtureName = isInput ? 'stdin-fd.js' : 'noop-fd.js'; - const {message} = await t.throwsAsync(execa(fixtureName, [`${fdNumber}`], getStdio(fdNumber, generator(input)(objectMode)))); - t.true(message.includes(getMessage(input))); -}; - -test('Generators with result.stdin cannot return an object if not in objectMode', testGeneratorReturn, 0, inputGenerator, foobarObject, false, true); -test('Generators with result.stdio[*] as input cannot return an object if not in objectMode', testGeneratorReturn, 3, inputGenerator, foobarObject, false, true); -test('The last generator with result.stdin cannot return an object even in objectMode', testGeneratorReturn, 0, lastInputGenerator, foobarObject, true, true); -test('The last generator with result.stdio[*] as input cannot return an object even in objectMode', testGeneratorReturn, 3, lastInputGenerator, foobarObject, true, true); -test('Generators with result.stdout cannot return an object if not in objectMode', testGeneratorReturn, 1, getOutputGenerator, foobarObject, false, false); -test('Generators with result.stderr cannot return an object if not in objectMode', testGeneratorReturn, 2, getOutputGenerator, foobarObject, false, false); -test('Generators with result.stdio[*] as output cannot return an object if not in objectMode', testGeneratorReturn, 3, getOutputGenerator, foobarObject, false, false); -test('Generators with result.stdin cannot return null if not in objectMode', testGeneratorReturn, 0, inputGenerator, null, false, true); -test('Generators with result.stdin cannot return null if in objectMode', testGeneratorReturn, 0, inputGenerator, null, true, true); -test('Generators with result.stdout cannot return null if not in objectMode', testGeneratorReturn, 1, getOutputGenerator, null, false, false); -test('Generators with result.stdout cannot return null if in objectMode', testGeneratorReturn, 1, getOutputGenerator, null, true, false); -test('Generators with result.stdin cannot return undefined if not in objectMode', testGeneratorReturn, 0, inputGenerator, undefined, false, true); -test('Generators with result.stdin cannot return undefined if in objectMode', testGeneratorReturn, 0, inputGenerator, undefined, true, true); -test('Generators with result.stdout cannot return undefined if not in objectMode', testGeneratorReturn, 1, getOutputGenerator, undefined, false, false); -test('Generators with result.stdout cannot return undefined if in objectMode', testGeneratorReturn, 1, getOutputGenerator, undefined, true, false); - -// eslint-disable-next-line max-params -const testGeneratorReturnSync = (t, fdNumber, generator, input, objectMode, isInput) => { - const fixtureName = isInput ? 'stdin-fd.js' : 'noop-fd.js'; - const {message} = t.throws(() => { - execaSync(fixtureName, [`${fdNumber}`], getStdio(fdNumber, generator(input)(objectMode))); - }); - t.true(message.includes(getMessage(input))); -}; - -test('Generators with result.stdin cannot return an object if not in objectMode, sync', testGeneratorReturnSync, 0, inputGenerator, foobarObject, false, true); -test('The last generator with result.stdin cannot return an object even in objectMode, sync', testGeneratorReturnSync, 0, lastInputGenerator, foobarObject, true, true); -test('Generators with result.stdout cannot return an object if not in objectMode, sync', testGeneratorReturnSync, 1, getOutputGenerator, foobarObject, false, false); -test('Generators with result.stderr cannot return an object if not in objectMode, sync', testGeneratorReturnSync, 2, getOutputGenerator, foobarObject, false, false); -test('Generators with result.stdio[*] as output cannot return an object if not in objectMode, sync', testGeneratorReturnSync, 3, getOutputGenerator, foobarObject, false, false); -test('Generators with result.stdin cannot return null if not in objectMode, sync', testGeneratorReturnSync, 0, inputGenerator, null, false, true); -test('Generators with result.stdin cannot return null if in objectMode, sync', testGeneratorReturnSync, 0, inputGenerator, null, true, true); -test('Generators with result.stdout cannot return null if not in objectMode, sync', testGeneratorReturnSync, 1, getOutputGenerator, null, false, false); -test('Generators with result.stdout cannot return null if in objectMode, sync', testGeneratorReturnSync, 1, getOutputGenerator, null, true, false); -test('Generators with result.stdin cannot return undefined if not in objectMode, sync', testGeneratorReturnSync, 0, inputGenerator, undefined, false, true); -test('Generators with result.stdin cannot return undefined if in objectMode, sync', testGeneratorReturnSync, 0, inputGenerator, undefined, true, true); -test('Generators with result.stdout cannot return undefined if not in objectMode, sync', testGeneratorReturnSync, 1, getOutputGenerator, undefined, false, false); -test('Generators with result.stdout cannot return undefined if in objectMode, sync', testGeneratorReturnSync, 1, getOutputGenerator, undefined, true, false); - -test('Generators "final" return value is validated', async t => { - await t.throwsAsync( - execa('noop.js', {stdout: convertTransformToFinal(getOutputGenerator(null)(true), true)}), - {message: /not be called at all/}, - ); -}); - -test('Generators "final" return value is validated, sync', t => { - t.throws(() => { - execaSync('noop.js', {stdout: convertTransformToFinal(getOutputGenerator(null)(true), true)}); - }, {message: /not be called at all/}); -}); diff --git a/test/verbose/a.sh b/test/verbose/a.sh new file mode 100644 index 0000000000..ff1e63841c --- /dev/null +++ b/test/verbose/a.sh @@ -0,0 +1,2 @@ +#!/bin/bash +echo oh diff --git a/test/verbose/complete.js b/test/verbose/complete.js index b37e4b6eb1..a54c9f6cb0 100644 --- a/test/verbose/complete.js +++ b/test/verbose/complete.js @@ -1,129 +1,12 @@ -import {stripVTControlCharacters} from 'node:util'; +import {fileURLToPath} from 'node:url'; +import path from 'node:path'; import test from 'ava'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString} from '../helpers/input.js'; -import {nestedSubprocess} from '../helpers/nested.js'; -import { - runErrorSubprocess, - runWarningSubprocess, - runEarlyErrorSubprocess, - getCompletionLine, - getCompletionLines, - testTimestamp, - getVerboseOption, - stdoutNoneOption, - stdoutShortOption, - stdoutFullOption, - stderrNoneOption, - stderrShortOption, - stderrFullOption, - fd3NoneOption, - fd3ShortOption, - fd3FullOption, - ipcNoneOption, - ipcShortOption, - ipcFullOption, -} from '../helpers/verbose.js'; - -setFixtureDirectory(); - -const testPrintCompletion = async (t, verbose, isSync) => { - const {stderr} = await nestedSubprocess('noop.js', [foobarString], {verbose, isSync}); - t.is(getCompletionLine(stderr), `${testTimestamp} [0] √ (done in 0ms)`); -}; - -test('Prints completion, verbose "short"', testPrintCompletion, 'short', false); -test('Prints completion, verbose "full"', testPrintCompletion, 'full', false); -test('Prints completion, verbose "short", fd-specific stdout', testPrintCompletion, stdoutShortOption, false); -test('Prints completion, verbose "full", fd-specific stdout', testPrintCompletion, stdoutFullOption, false); -test('Prints completion, verbose "short", fd-specific stderr', testPrintCompletion, stderrShortOption, false); -test('Prints completion, verbose "full", fd-specific stderr', testPrintCompletion, stderrFullOption, false); -test('Prints completion, verbose "short", fd-specific fd3', testPrintCompletion, fd3ShortOption, false); -test('Prints completion, verbose "full", fd-specific fd3', testPrintCompletion, fd3FullOption, false); -test('Prints completion, verbose "short", fd-specific ipc', testPrintCompletion, ipcShortOption, false); -test('Prints completion, verbose "full", fd-specific ipc', testPrintCompletion, ipcFullOption, false); -test('Prints completion, verbose "short", sync', testPrintCompletion, 'short', true); -test('Prints completion, verbose "full", sync', testPrintCompletion, 'full', true); -test('Prints completion, verbose "short", fd-specific stdout, sync', testPrintCompletion, stdoutShortOption, true); -test('Prints completion, verbose "full", fd-specific stdout, sync', testPrintCompletion, stdoutFullOption, true); -test('Prints completion, verbose "short", fd-specific stderr, sync', testPrintCompletion, stderrShortOption, true); -test('Prints completion, verbose "full", fd-specific stderr, sync', testPrintCompletion, stderrFullOption, true); -test('Prints completion, verbose "short", fd-specific fd3, sync', testPrintCompletion, fd3ShortOption, true); -test('Prints completion, verbose "full", fd-specific fd3, sync', testPrintCompletion, fd3FullOption, true); -test('Prints completion, verbose "short", fd-specific ipc, sync', testPrintCompletion, ipcShortOption, true); -test('Prints completion, verbose "full", fd-specific ipc, sync', testPrintCompletion, ipcFullOption, true); - -const testNoPrintCompletion = async (t, verbose, isSync) => { - const {stderr} = await nestedSubprocess('noop.js', [foobarString], {verbose, isSync}); - t.is(stderr, ''); -}; - -test('Does not print completion, verbose "none"', testNoPrintCompletion, 'none', false); -test('Does not print completion, verbose default"', testNoPrintCompletion, undefined, false); -test('Does not print completion, verbose "none", fd-specific stdout', testNoPrintCompletion, stdoutNoneOption, false); -test('Does not print completion, verbose "none", fd-specific stderr', testNoPrintCompletion, stderrNoneOption, false); -test('Does not print completion, verbose "none", fd-specific fd3', testNoPrintCompletion, fd3NoneOption, false); -test('Does not print completion, verbose "none", fd-specific ipc', testNoPrintCompletion, ipcNoneOption, false); -test('Does not print completion, verbose default", fd-specific', testNoPrintCompletion, {}, false); -test('Does not print completion, verbose "none", sync', testNoPrintCompletion, 'none', true); -test('Does not print completion, verbose default", sync', testNoPrintCompletion, undefined, true); -test('Does not print completion, verbose "none", fd-specific stdout, sync', testNoPrintCompletion, stdoutNoneOption, true); -test('Does not print completion, verbose "none", fd-specific stderr, sync', testNoPrintCompletion, stderrNoneOption, true); -test('Does not print completion, verbose "none", fd-specific fd3, sync', testNoPrintCompletion, fd3NoneOption, true); -test('Does not print completion, verbose "none", fd-specific ipc, sync', testNoPrintCompletion, ipcNoneOption, true); -test('Does not print completion, verbose default", fd-specific, sync', testNoPrintCompletion, {}, true); - -const testPrintCompletionError = async (t, isSync) => { - const stderr = await runErrorSubprocess(t, 'short', isSync); - t.is(getCompletionLine(stderr), `${testTimestamp} [0] × (done in 0ms)`); -}; - -test('Prints completion after errors', testPrintCompletionError, false); -test('Prints completion after errors, sync', testPrintCompletionError, true); - -const testPrintCompletionWarning = async (t, isSync) => { - const stderr = await runWarningSubprocess(t, isSync); - t.is(getCompletionLine(stderr), `${testTimestamp} [0] ‼ (done in 0ms)`); -}; - -test('Prints completion after errors, "reject" false', testPrintCompletionWarning, false); -test('Prints completion after errors, "reject" false, sync', testPrintCompletionWarning, true); - -const testPrintCompletionEarly = async (t, isSync) => { - const stderr = await runEarlyErrorSubprocess(t, isSync); - t.is(getCompletionLine(stderr), undefined); -}; - -test('Prints completion after early validation errors', testPrintCompletionEarly, false); -test('Prints completion after early validation errors, sync', testPrintCompletionEarly, true); - -test.serial('Prints duration', async t => { - const {stderr} = await nestedSubprocess('delay.js', ['1000'], {verbose: 'short'}); - t.regex(stripVTControlCharacters(stderr).split('\n').at(-1), /\(done in [\d.]+s\)/); +import {execa} from '../../index.js'; + +test('debug', async t => { + await execa({ + cwd: path.dirname(fileURLToPath(import.meta.url)), + stdout: 'inherit', + })`wsl -e ${'a.sh'}`; + t.pass(); }); - -const testPipeDuration = async (t, parentFixture, sourceVerbose, destinationVerbose) => { - const {stderr} = await nestedSubprocess('noop.js', [foobarString], { - parentFixture, - sourceOptions: getVerboseOption(sourceVerbose), - destinationFile: 'stdin.js', - destinationOptions: getVerboseOption(destinationVerbose), - }); - - const lines = getCompletionLines(stderr); - t.is(lines.includes(`${testTimestamp} [0] √ (done in 0ms)`), sourceVerbose || destinationVerbose); - t.is(lines.includes(`${testTimestamp} [1] √ (done in 0ms)`), sourceVerbose && destinationVerbose); -}; - -test('Prints both durations piped with .pipe("file")', testPipeDuration, 'nested-pipe-file.js', true, true); -test('Prints both durations piped with .pipe`command`', testPipeDuration, 'nested-pipe-script.js', true, true); -test('Prints both durations piped with .pipe(subprocess)', testPipeDuration, 'nested-pipe-subprocesses.js', true, true); -test('Prints first duration piped with .pipe("file")', testPipeDuration, 'nested-pipe-file.js', true, false); -test('Prints first duration piped with .pipe`command`', testPipeDuration, 'nested-pipe-script.js', true, false); -test('Prints first duration piped with .pipe(subprocess)', testPipeDuration, 'nested-pipe-subprocesses.js', true, false); -test('Prints second duration piped with .pipe("file")', testPipeDuration, 'nested-pipe-file.js', false, true); -test('Prints second duration piped with .pipe`command`', testPipeDuration, 'nested-pipe-script.js', false, true); -test('Prints second duration piped with .pipe(subprocess)', testPipeDuration, 'nested-pipe-subprocesses.js', false, true); -test('Prints neither durations piped with .pipe("file")', testPipeDuration, 'nested-pipe-file.js', false, false); -test('Prints neither durations piped with .pipe`command`', testPipeDuration, 'nested-pipe-script.js', false, false); -test('Prints neither durations piped with .pipe(subprocess)', testPipeDuration, 'nested-pipe-subprocesses.js', false, false); diff --git a/test/verbose/custom-command.js b/test/verbose/custom-command.js deleted file mode 100644 index 9c47139df0..0000000000 --- a/test/verbose/custom-command.js +++ /dev/null @@ -1,97 +0,0 @@ -import test from 'ava'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {fullStdio} from '../helpers/stdio.js'; -import { - QUOTE, - getNormalizedLine, - testTimestamp, - runVerboseSubprocess, -} from '../helpers/verbose.js'; - -setFixtureDirectory(); - -const testPrintCommandCustom = async (t, fdNumber, isSync) => { - const {stderr} = await runVerboseSubprocess({ - optionsFixture: 'custom-print.js', - isSync, - type: 'command', - fdNumber, - }); - t.is(getNormalizedLine(stderr), `${testTimestamp} [0] $ noop-verbose.js ${QUOTE}. .${QUOTE}`); -}; - -test('Prints command, verbose custom', testPrintCommandCustom, undefined, false); -test('Prints command, verbose custom, fd-specific stdout', testPrintCommandCustom, 'stdout', false); -test('Prints command, verbose custom, fd-specific stderr', testPrintCommandCustom, 'stderr', false); -test('Prints command, verbose custom, fd-specific fd3', testPrintCommandCustom, 'fd3', false); -test('Prints command, verbose custom, fd-specific ipc', testPrintCommandCustom, 'ipc', false); -test('Prints command, verbose custom, sync', testPrintCommandCustom, undefined, true); -test('Prints command, verbose custom, fd-specific stdout, sync', testPrintCommandCustom, 'stdout', true); -test('Prints command, verbose custom, fd-specific stderr, sync', testPrintCommandCustom, 'stderr', true); -test('Prints command, verbose custom, fd-specific fd3, sync', testPrintCommandCustom, 'fd3', true); -test('Prints command, verbose custom, fd-specific ipc, sync', testPrintCommandCustom, 'ipc', true); - -const testPrintCommandOrder = async (t, fdNumber, secondFdNumber, hasOutput) => { - const {stderr} = await runVerboseSubprocess({ - optionsFixture: 'custom-print-multiple.js', - type: 'command', - fdNumber, - secondFdNumber, - ...fullStdio, - }); - - if (hasOutput) { - t.is(getNormalizedLine(stderr), `${testTimestamp} [0] $ noop-verbose.js ${QUOTE}. .${QUOTE}`); - } else { - t.is(stderr, ''); - } -}; - -test('Prints command, verbose custom, fd-specific stdout+stderr', testPrintCommandOrder, 'stdout', 'stderr', true); -test('Prints command, verbose custom, fd-specific stderr+stdout', testPrintCommandOrder, 'stderr', 'stdout', false); -test('Prints command, verbose custom, fd-specific stdout+fd3', testPrintCommandOrder, 'stdout', 'fd3', true); -test('Prints command, verbose custom, fd-specific fd3+stdout', testPrintCommandOrder, 'fd3', 'stdout', false); -test('Prints command, verbose custom, fd-specific stdout+ipc', testPrintCommandOrder, 'stdout', 'ipc', true); -test('Prints command, verbose custom, fd-specific ipc+stdout', testPrintCommandOrder, 'ipc', 'stdout', false); -test('Prints command, verbose custom, fd-specific stderr+fd3', testPrintCommandOrder, 'stderr', 'fd3', true); -test('Prints command, verbose custom, fd-specific fd3+stderr', testPrintCommandOrder, 'fd3', 'stderr', false); -test('Prints command, verbose custom, fd-specific stderr+ipc', testPrintCommandOrder, 'stderr', 'ipc', true); -test('Prints command, verbose custom, fd-specific ipc+stderr', testPrintCommandOrder, 'ipc', 'stderr', false); -test('Prints command, verbose custom, fd-specific fd3+ipc', testPrintCommandOrder, 'fd3', 'ipc', true); -test('Prints command, verbose custom, fd-specific ipc+fd3', testPrintCommandOrder, 'ipc', 'fd3', false); - -const testPrintCommandFunction = async (t, fdNumber, secondFdNumber) => { - const {stderr} = await runVerboseSubprocess({ - optionsFixture: 'custom-print-function.js', - type: 'command', - fdNumber, - secondFdNumber, - ...fullStdio, - }); - t.is(getNormalizedLine(stderr), `${testTimestamp} [0] $ noop-verbose.js ${QUOTE}. .${QUOTE}`); -}; - -test('Prints command, verbose custom, fd-specific stdout+stderr, single function', testPrintCommandFunction, 'stdout', 'stderr'); -test('Prints command, verbose custom, fd-specific stderr+stdout, single function', testPrintCommandFunction, 'stderr', 'stdout'); -test('Prints command, verbose custom, fd-specific stdout+fd3, single function', testPrintCommandFunction, 'stdout', 'fd3'); -test('Prints command, verbose custom, fd-specific fd3+stdout, single function', testPrintCommandFunction, 'fd3', 'stdout'); -test('Prints command, verbose custom, fd-specific stdout+ipc, single function', testPrintCommandFunction, 'stdout', 'ipc'); -test('Prints command, verbose custom, fd-specific ipc+stdout, single function', testPrintCommandFunction, 'ipc', 'stdout'); -test('Prints command, verbose custom, fd-specific stderr+fd3, single function', testPrintCommandFunction, 'stderr', 'fd3'); -test('Prints command, verbose custom, fd-specific fd3+stderr, single function', testPrintCommandFunction, 'fd3', 'stderr'); -test('Prints command, verbose custom, fd-specific stderr+ipc, single function', testPrintCommandFunction, 'stderr', 'ipc'); -test('Prints command, verbose custom, fd-specific ipc+stderr, single function', testPrintCommandFunction, 'ipc', 'stderr'); -test('Prints command, verbose custom, fd-specific fd3+ipc, single function', testPrintCommandFunction, 'fd3', 'ipc'); -test('Prints command, verbose custom, fd-specific ipc+fd3, single function', testPrintCommandFunction, 'ipc', 'fd3'); - -const testVerboseMessage = async (t, isSync) => { - const {stderr} = await runVerboseSubprocess({ - isSync, - type: 'command', - eventProperty: 'message', - }); - t.is(getNormalizedLine(stderr), `noop-verbose.js ${QUOTE}. .${QUOTE}`); -}; - -test('"verbose" function receives verboseObject.message', testVerboseMessage, false); -test('"verbose" function receives verboseObject.message, sync', testVerboseMessage, true); diff --git a/test/verbose/custom-common.js b/test/verbose/custom-common.js deleted file mode 100644 index 79ac79d3d2..0000000000 --- a/test/verbose/custom-common.js +++ /dev/null @@ -1,48 +0,0 @@ -import test from 'ava'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString} from '../helpers/input.js'; -import {nestedSubprocess} from '../helpers/nested.js'; -import {QUOTE, getCommandLine, testTimestamp} from '../helpers/verbose.js'; - -setFixtureDirectory(); - -const testCustomReturn = async (t, verboseOutput, expectedOutput) => { - const {stderr} = await nestedSubprocess( - 'empty.js', - {optionsFixture: 'custom-return.js', optionsInput: {verboseOutput}}, - {stripFinalNewline: false}, - ); - t.is(stderr, expectedOutput); -}; - -test('"verbose" returning a string prints it', testCustomReturn, `${foobarString}\n`, `${foobarString}\n`); -test('"verbose" returning a string without a newline adds it', testCustomReturn, foobarString, `${foobarString}\n`); -test('"verbose" returning a string with multiple newlines keeps them', testCustomReturn, `${foobarString}\n\n`, `${foobarString}\n\n`); -test('"verbose" returning an empty string prints an empty line', testCustomReturn, '', '\n'); -test('"verbose" returning undefined ignores it', testCustomReturn, undefined, ''); -test('"verbose" returning a number ignores it', testCustomReturn, 0, ''); -test('"verbose" returning a bigint ignores it', testCustomReturn, 0n, ''); -test('"verbose" returning a boolean ignores it', testCustomReturn, true, ''); -test('"verbose" returning an object ignores it', testCustomReturn, {}, ''); -test('"verbose" returning an array ignores it', testCustomReturn, [], ''); - -test('"verbose" receives verboseLine string as first argument', async t => { - const {stderr} = await nestedSubprocess('noop.js', [foobarString], {optionsFixture: 'custom-uppercase.js'}); - t.is(getCommandLine(stderr), `${testTimestamp} [0] $ NOOP.js ${foobarString}`); -}); - -test('"verbose" can print as JSON', async t => { - const {stderr} = await nestedSubprocess('noop.js', ['. .'], {optionsFixture: 'custom-json.js', type: 'duration', reject: false}); - const {type, message, escapedCommand, commandId, timestamp, piped, result, options} = JSON.parse(stderr); - t.is(type, 'duration'); - t.true(message.includes('done in')); - t.is(escapedCommand, `noop.js ${QUOTE}. .${QUOTE}`); - t.is(commandId, '0'); - t.true(Number.isInteger(new Date(timestamp).getTime())); - t.false(piped); - t.false(result.failed); - t.is(result.exitCode, 0); - t.is(result.stdout, '. .'); - t.is(result.stderr, ''); - t.false(options.reject); -}); diff --git a/test/verbose/custom-complete.js b/test/verbose/custom-complete.js deleted file mode 100644 index 58d57d5b2a..0000000000 --- a/test/verbose/custom-complete.js +++ /dev/null @@ -1,92 +0,0 @@ -import test from 'ava'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {fullStdio} from '../helpers/stdio.js'; -import {getNormalizedLine, testTimestamp, runVerboseSubprocess} from '../helpers/verbose.js'; - -setFixtureDirectory(); - -const testPrintCompletionCustom = async (t, fdNumber, isSync) => { - const {stderr} = await runVerboseSubprocess({ - optionsFixture: 'custom-print.js', - isSync, - type: 'duration', - fdNumber, - }); - t.is(getNormalizedLine(stderr), `${testTimestamp} [0] × (done in 0ms)`); -}; - -test('Prints completion, verbose custom', testPrintCompletionCustom, undefined, false); -test('Prints completion, verbose custom, fd-specific stdout', testPrintCompletionCustom, 'stdout', false); -test('Prints completion, verbose custom, fd-specific stderr', testPrintCompletionCustom, 'stderr', false); -test('Prints completion, verbose custom, fd-specific fd3', testPrintCompletionCustom, 'fd3', false); -test('Prints completion, verbose custom, fd-specific ipc', testPrintCompletionCustom, 'ipc', false); -test('Prints completion, verbose custom, sync', testPrintCompletionCustom, undefined, true); -test('Prints completion, verbose custom, fd-specific stdout, sync', testPrintCompletionCustom, 'stdout', true); -test('Prints completion, verbose custom, fd-specific stderr, sync', testPrintCompletionCustom, 'stderr', true); -test('Prints completion, verbose custom, fd-specific fd3, sync', testPrintCompletionCustom, 'fd3', true); -test('Prints completion, verbose custom, fd-specific ipc, sync', testPrintCompletionCustom, 'ipc', true); - -const testPrintCompletionOrder = async (t, fdNumber, secondFdNumber, hasOutput) => { - const {stderr} = await runVerboseSubprocess({ - optionsFixture: 'custom-print-multiple.js', - type: 'duration', - fdNumber, - secondFdNumber, - ...fullStdio, - }); - - if (hasOutput) { - t.is(getNormalizedLine(stderr), `${testTimestamp} [0] × (done in 0ms)`); - } else { - t.is(stderr, ''); - } -}; - -test('Prints completion, verbose custom, fd-specific stdout+stderr', testPrintCompletionOrder, 'stdout', 'stderr', true); -test('Prints completion, verbose custom, fd-specific stderr+stdout', testPrintCompletionOrder, 'stderr', 'stdout', false); -test('Prints completion, verbose custom, fd-specific stdout+fd3', testPrintCompletionOrder, 'stdout', 'fd3', true); -test('Prints completion, verbose custom, fd-specific fd3+stdout', testPrintCompletionOrder, 'fd3', 'stdout', false); -test('Prints completion, verbose custom, fd-specific stdout+ipc', testPrintCompletionOrder, 'stdout', 'ipc', true); -test('Prints completion, verbose custom, fd-specific ipc+stdout', testPrintCompletionOrder, 'ipc', 'stdout', false); -test('Prints completion, verbose custom, fd-specific stderr+fd3', testPrintCompletionOrder, 'stderr', 'fd3', true); -test('Prints completion, verbose custom, fd-specific fd3+stderr', testPrintCompletionOrder, 'fd3', 'stderr', false); -test('Prints completion, verbose custom, fd-specific stderr+ipc', testPrintCompletionOrder, 'stderr', 'ipc', true); -test('Prints completion, verbose custom, fd-specific ipc+stderr', testPrintCompletionOrder, 'ipc', 'stderr', false); -test('Prints completion, verbose custom, fd-specific fd3+ipc', testPrintCompletionOrder, 'fd3', 'ipc', true); -test('Prints completion, verbose custom, fd-specific ipc+fd3', testPrintCompletionOrder, 'ipc', 'fd3', false); - -const testPrintCompletionFunction = async (t, fdNumber, secondFdNumber) => { - const {stderr} = await runVerboseSubprocess({ - optionsFixture: 'custom-print-function.js', - type: 'duration', - fdNumber, - secondFdNumber, - ...fullStdio, - }); - t.is(getNormalizedLine(stderr), `${testTimestamp} [0] × (done in 0ms)`); -}; - -test('Prints completion, verbose custom, fd-specific stdout+stderr, single function', testPrintCompletionFunction, 'stdout', 'stderr'); -test('Prints completion, verbose custom, fd-specific stderr+stdout, single function', testPrintCompletionFunction, 'stderr', 'stdout'); -test('Prints completion, verbose custom, fd-specific stdout+fd3, single function', testPrintCompletionFunction, 'stdout', 'fd3'); -test('Prints completion, verbose custom, fd-specific fd3+stdout, single function', testPrintCompletionFunction, 'fd3', 'stdout'); -test('Prints completion, verbose custom, fd-specific stdout+ipc, single function', testPrintCompletionFunction, 'stdout', 'ipc'); -test('Prints completion, verbose custom, fd-specific ipc+stdout, single function', testPrintCompletionFunction, 'ipc', 'stdout'); -test('Prints completion, verbose custom, fd-specific stderr+fd3, single function', testPrintCompletionFunction, 'stderr', 'fd3'); -test('Prints completion, verbose custom, fd-specific fd3+stderr, single function', testPrintCompletionFunction, 'fd3', 'stderr'); -test('Prints completion, verbose custom, fd-specific stderr+ipc, single function', testPrintCompletionFunction, 'stderr', 'ipc'); -test('Prints completion, verbose custom, fd-specific ipc+stderr, single function', testPrintCompletionFunction, 'ipc', 'stderr'); -test('Prints completion, verbose custom, fd-specific fd3+ipc, single function', testPrintCompletionFunction, 'fd3', 'ipc'); -test('Prints completion, verbose custom, fd-specific ipc+fd3, single function', testPrintCompletionFunction, 'ipc', 'fd3'); - -const testVerboseMessage = async (t, isSync) => { - const {stderr} = await runVerboseSubprocess({ - isSync, - type: 'duration', - eventProperty: 'message', - }); - t.is(getNormalizedLine(stderr), '(done in 0ms)'); -}; - -test('"verbose" function receives verboseObject.message', testVerboseMessage, false); -test('"verbose" function receives verboseObject.message, sync', testVerboseMessage, true); diff --git a/test/verbose/custom-error.js b/test/verbose/custom-error.js deleted file mode 100644 index 77854e6925..0000000000 --- a/test/verbose/custom-error.js +++ /dev/null @@ -1,97 +0,0 @@ -import test from 'ava'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {fullStdio} from '../helpers/stdio.js'; -import { - QUOTE, - getNormalizedLine, - testTimestamp, - runVerboseSubprocess, -} from '../helpers/verbose.js'; - -setFixtureDirectory(); - -const testPrintErrorCustom = async (t, fdNumber, isSync) => { - const {stderr} = await runVerboseSubprocess({ - optionsFixture: 'custom-print.js', - isSync, - type: 'error', - fdNumber, - }); - t.is(getNormalizedLine(stderr), `${testTimestamp} [0] × Command failed with exit code 2: noop-verbose.js ${QUOTE}. .${QUOTE}`); -}; - -test('Prints error, verbose custom', testPrintErrorCustom, undefined, false); -test('Prints error, verbose custom, fd-specific stdout', testPrintErrorCustom, 'stdout', false); -test('Prints error, verbose custom, fd-specific stderr', testPrintErrorCustom, 'stderr', false); -test('Prints error, verbose custom, fd-specific fd3', testPrintErrorCustom, 'fd3', false); -test('Prints error, verbose custom, fd-specific ipc', testPrintErrorCustom, 'ipc', false); -test('Prints error, verbose custom, sync', testPrintErrorCustom, undefined, true); -test('Prints error, verbose custom, fd-specific stdout, sync', testPrintErrorCustom, 'stdout', true); -test('Prints error, verbose custom, fd-specific stderr, sync', testPrintErrorCustom, 'stderr', true); -test('Prints error, verbose custom, fd-specific fd3, sync', testPrintErrorCustom, 'fd3', true); -test('Prints error, verbose custom, fd-specific ipc, sync', testPrintErrorCustom, 'ipc', true); - -const testPrintErrorOrder = async (t, fdNumber, secondFdNumber, hasOutput) => { - const {stderr} = await runVerboseSubprocess({ - optionsFixture: 'custom-print-multiple.js', - type: 'error', - fdNumber, - secondFdNumber, - ...fullStdio, - }); - - if (hasOutput) { - t.is(getNormalizedLine(stderr), `${testTimestamp} [0] × Command failed with exit code 2: noop-verbose.js ${QUOTE}. .${QUOTE}`); - } else { - t.is(stderr, ''); - } -}; - -test('Prints error, verbose custom, fd-specific stdout+stderr', testPrintErrorOrder, 'stdout', 'stderr', true); -test('Prints error, verbose custom, fd-specific stderr+stdout', testPrintErrorOrder, 'stderr', 'stdout', false); -test('Prints error, verbose custom, fd-specific stdout+fd3', testPrintErrorOrder, 'stdout', 'fd3', true); -test('Prints error, verbose custom, fd-specific fd3+stdout', testPrintErrorOrder, 'fd3', 'stdout', false); -test('Prints error, verbose custom, fd-specific stdout+ipc', testPrintErrorOrder, 'stdout', 'ipc', true); -test('Prints error, verbose custom, fd-specific ipc+stdout', testPrintErrorOrder, 'ipc', 'stdout', false); -test('Prints error, verbose custom, fd-specific stderr+fd3', testPrintErrorOrder, 'stderr', 'fd3', true); -test('Prints error, verbose custom, fd-specific fd3+stderr', testPrintErrorOrder, 'fd3', 'stderr', false); -test('Prints error, verbose custom, fd-specific stderr+ipc', testPrintErrorOrder, 'stderr', 'ipc', true); -test('Prints error, verbose custom, fd-specific ipc+stderr', testPrintErrorOrder, 'ipc', 'stderr', false); -test('Prints error, verbose custom, fd-specific fd3+ipc', testPrintErrorOrder, 'fd3', 'ipc', true); -test('Prints error, verbose custom, fd-specific ipc+fd3', testPrintErrorOrder, 'ipc', 'fd3', false); - -const testPrintErrorFunction = async (t, fdNumber, secondFdNumber) => { - const {stderr} = await runVerboseSubprocess({ - optionsFixture: 'custom-print-function.js', - type: 'error', - fdNumber, - secondFdNumber, - ...fullStdio, - }); - t.is(getNormalizedLine(stderr), `${testTimestamp} [0] × Command failed with exit code 2: noop-verbose.js ${QUOTE}. .${QUOTE}`); -}; - -test('Prints error, verbose custom, fd-specific stdout+stderr, single function', testPrintErrorFunction, 'stdout', 'stderr'); -test('Prints error, verbose custom, fd-specific stderr+stdout, single function', testPrintErrorFunction, 'stderr', 'stdout'); -test('Prints error, verbose custom, fd-specific stdout+fd3, single function', testPrintErrorFunction, 'stdout', 'fd3'); -test('Prints error, verbose custom, fd-specific fd3+stdout, single function', testPrintErrorFunction, 'fd3', 'stdout'); -test('Prints error, verbose custom, fd-specific stdout+ipc, single function', testPrintErrorFunction, 'stdout', 'ipc'); -test('Prints error, verbose custom, fd-specific ipc+stdout, single function', testPrintErrorFunction, 'ipc', 'stdout'); -test('Prints error, verbose custom, fd-specific stderr+fd3, single function', testPrintErrorFunction, 'stderr', 'fd3'); -test('Prints error, verbose custom, fd-specific fd3+stderr, single function', testPrintErrorFunction, 'fd3', 'stderr'); -test('Prints error, verbose custom, fd-specific stderr+ipc, single function', testPrintErrorFunction, 'stderr', 'ipc'); -test('Prints error, verbose custom, fd-specific ipc+stderr, single function', testPrintErrorFunction, 'ipc', 'stderr'); -test('Prints error, verbose custom, fd-specific fd3+ipc, single function', testPrintErrorFunction, 'fd3', 'ipc'); -test('Prints error, verbose custom, fd-specific ipc+fd3, single function', testPrintErrorFunction, 'ipc', 'fd3'); - -const testVerboseMessage = async (t, isSync) => { - const {stderr} = await runVerboseSubprocess({ - isSync, - type: 'error', - eventProperty: 'message', - }); - t.is(stderr, `Command failed with exit code 2: noop-verbose.js ${QUOTE}. .${QUOTE}`); -}; - -test('"verbose" function receives verboseObject.message', testVerboseMessage, false); -test('"verbose" function receives verboseObject.message, sync', testVerboseMessage, true); diff --git a/test/verbose/custom-event.js b/test/verbose/custom-event.js deleted file mode 100644 index d9b6ab9f3d..0000000000 --- a/test/verbose/custom-event.js +++ /dev/null @@ -1,57 +0,0 @@ -import test from 'ava'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {runVerboseSubprocess} from '../helpers/verbose.js'; - -setFixtureDirectory(); - -const testVerboseType = async (t, type, isSync) => { - const {stderr} = await runVerboseSubprocess({isSync, type, eventProperty: 'type'}); - t.is(stderr, type); -}; - -test('"verbose" function receives verboseObject.type "command"', testVerboseType, 'command', false); -test('"verbose" function receives verboseObject.type "output"', testVerboseType, 'output', false); -test('"verbose" function receives verboseObject.type "ipc"', testVerboseType, 'ipc', false); -test('"verbose" function receives verboseObject.type "error"', testVerboseType, 'error', false); -test('"verbose" function receives verboseObject.type "duration"', testVerboseType, 'duration', false); -test('"verbose" function receives verboseObject.type "command", sync', testVerboseType, 'command', true); -test('"verbose" function receives verboseObject.type "output", sync', testVerboseType, 'output', true); -test('"verbose" function receives verboseObject.type "error", sync', testVerboseType, 'error', true); -test('"verbose" function receives verboseObject.type "duration", sync', testVerboseType, 'duration', true); - -const testVerboseTimestamp = async (t, type, isSync) => { - const {stderr} = await runVerboseSubprocess({isSync, type, eventProperty: 'timestamp'}); - t.true(Number.isInteger(new Date(stderr).getTime())); -}; - -test('"verbose" function receives verboseObject.timestamp, "command"', testVerboseTimestamp, 'command', false); -test('"verbose" function receives verboseObject.timestamp, "output"', testVerboseTimestamp, 'output', false); -test('"verbose" function receives verboseObject.timestamp, "ipc"', testVerboseTimestamp, 'ipc', false); -test('"verbose" function receives verboseObject.timestamp, "error"', testVerboseTimestamp, 'error', false); -test('"verbose" function receives verboseObject.timestamp, "duration"', testVerboseTimestamp, 'duration', false); -test('"verbose" function receives verboseObject.timestamp, "command", sync', testVerboseTimestamp, 'command', true); -test('"verbose" function receives verboseObject.timestamp, "output", sync', testVerboseTimestamp, 'output', true); -test('"verbose" function receives verboseObject.timestamp, "error", sync', testVerboseTimestamp, 'error', true); -test('"verbose" function receives verboseObject.timestamp, "duration", sync', testVerboseTimestamp, 'duration', true); - -const testVerbosePiped = async (t, type, isSync, expectedOutputs) => { - const {stderr} = await runVerboseSubprocess({ - isSync, - type, - parentFixture: 'nested-pipe-verbose.js', - destinationFile: 'noop-verbose.js', - destinationArguments: ['. . .'], - eventProperty: 'piped', - }); - t.true(expectedOutputs.map(expectedOutput => expectedOutput.join('\n')).includes(stderr)); -}; - -test('"verbose" function receives verboseObject.piped, "command"', testVerbosePiped, 'command', false, [[false, true]]); -test('"verbose" function receives verboseObject.piped, "output"', testVerbosePiped, 'output', false, [[true]]); -test('"verbose" function receives verboseObject.piped, "ipc"', testVerbosePiped, 'ipc', false, [[false, true], [true, false]]); -test('"verbose" function receives verboseObject.piped, "error"', testVerbosePiped, 'error', false, [[false, true], [true, false]]); -test('"verbose" function receives verboseObject.piped, "duration"', testVerbosePiped, 'duration', false, [[false, true], [true, false]]); -test('"verbose" function receives verboseObject.piped, "command", sync', testVerbosePiped, 'command', true, [[false, true]]); -test('"verbose" function receives verboseObject.piped, "output", sync', testVerbosePiped, 'output', true, [[true]]); -test('"verbose" function receives verboseObject.piped, "error", sync', testVerbosePiped, 'error', true, [[false, true], [true, false]]); -test('"verbose" function receives verboseObject.piped, "duration", sync', testVerbosePiped, 'duration', true, [[false, true], [true, false]]); diff --git a/test/verbose/custom-id.js b/test/verbose/custom-id.js deleted file mode 100644 index d7bbb9878c..0000000000 --- a/test/verbose/custom-id.js +++ /dev/null @@ -1,35 +0,0 @@ -import test from 'ava'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {QUOTE, runVerboseSubprocess} from '../helpers/verbose.js'; - -setFixtureDirectory(); - -const testVerboseCommandId = async (t, type, isSync) => { - const {stderr} = await runVerboseSubprocess({isSync, type, eventProperty: 'commandId'}); - t.is(stderr, '0'); -}; - -test('"verbose" function receives verboseObject.commandId, "command"', testVerboseCommandId, 'command', false); -test('"verbose" function receives verboseObject.commandId, "output"', testVerboseCommandId, 'output', false); -test('"verbose" function receives verboseObject.commandId, "ipc"', testVerboseCommandId, 'ipc', false); -test('"verbose" function receives verboseObject.commandId, "error"', testVerboseCommandId, 'error', false); -test('"verbose" function receives verboseObject.commandId, "duration"', testVerboseCommandId, 'duration', false); -test('"verbose" function receives verboseObject.commandId, "command", sync', testVerboseCommandId, 'command', true); -test('"verbose" function receives verboseObject.commandId, "output", sync', testVerboseCommandId, 'output', true); -test('"verbose" function receives verboseObject.commandId, "error", sync', testVerboseCommandId, 'error', true); -test('"verbose" function receives verboseObject.commandId, "duration", sync', testVerboseCommandId, 'duration', true); - -const testVerboseEscapedCommand = async (t, type, isSync) => { - const {stderr} = await runVerboseSubprocess({isSync, type, eventProperty: 'escapedCommand'}); - t.is(stderr, `noop-verbose.js ${QUOTE}. .${QUOTE}`); -}; - -test('"verbose" function receives verboseObject.escapedCommand, "command"', testVerboseEscapedCommand, 'command', false); -test('"verbose" function receives verboseObject.escapedCommand, "output"', testVerboseEscapedCommand, 'output', false); -test('"verbose" function receives verboseObject.escapedCommand, "ipc"', testVerboseEscapedCommand, 'ipc', false); -test('"verbose" function receives verboseObject.escapedCommand, "error"', testVerboseEscapedCommand, 'error', false); -test('"verbose" function receives verboseObject.escapedCommand, "duration"', testVerboseEscapedCommand, 'duration', false); -test('"verbose" function receives verboseObject.escapedCommand, "command", sync', testVerboseEscapedCommand, 'command', true); -test('"verbose" function receives verboseObject.escapedCommand, "output", sync', testVerboseEscapedCommand, 'output', true); -test('"verbose" function receives verboseObject.escapedCommand, "error", sync', testVerboseEscapedCommand, 'error', true); -test('"verbose" function receives verboseObject.escapedCommand, "duration", sync', testVerboseEscapedCommand, 'duration', true); diff --git a/test/verbose/custom-ipc.js b/test/verbose/custom-ipc.js deleted file mode 100644 index 48cbb67369..0000000000 --- a/test/verbose/custom-ipc.js +++ /dev/null @@ -1,119 +0,0 @@ -import {inspect} from 'node:util'; -import test from 'ava'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {fullStdio} from '../helpers/stdio.js'; -import { - getNormalizedLine, - getNormalizedLines, - testTimestamp, - runVerboseSubprocess, -} from '../helpers/verbose.js'; -import {nestedSubprocess} from '../helpers/nested.js'; -import {foobarObject} from '../helpers/input.js'; - -setFixtureDirectory(); - -const testPrintIpcCustom = async (t, fdNumber, hasOutput) => { - const {stderr} = await runVerboseSubprocess({ - optionsFixture: 'custom-print.js', - type: 'ipc', - fdNumber, - ...fullStdio, - }); - - if (hasOutput) { - t.is(getNormalizedLine(stderr), `${testTimestamp} [0] * . .`); - } else { - t.is(stderr, ''); - } -}; - -test('Prints IPC, verbose custom', testPrintIpcCustom, undefined, true); -test('Prints IPC, verbose custom, fd-specific stdout', testPrintIpcCustom, 'stdout', false); -test('Prints IPC, verbose custom, fd-specific stderr', testPrintIpcCustom, 'stderr', false); -test('Prints IPC, verbose custom, fd-specific fd3', testPrintIpcCustom, 'fd3', false); -test('Prints IPC, verbose custom, fd-specific ipc', testPrintIpcCustom, 'ipc', true); - -const testPrintIpcOrder = async (t, fdNumber, secondFdNumber, hasOutput) => { - const {stderr} = await runVerboseSubprocess({ - optionsFixture: 'custom-print-multiple.js', - type: 'ipc', - fdNumber, - secondFdNumber, - ...fullStdio, - }); - - if (hasOutput) { - t.is(getNormalizedLine(stderr), `${testTimestamp} [0] * . .`); - } else { - t.is(stderr, ''); - } -}; - -test('Prints IPC, verbose custom, fd-specific stdout+stderr', testPrintIpcOrder, 'stdout', 'stderr', false); -test('Prints IPC, verbose custom, fd-specific stderr+stdout', testPrintIpcOrder, 'stderr', 'stdout', false); -test('Prints IPC, verbose custom, fd-specific stdout+fd3', testPrintIpcOrder, 'stdout', 'fd3', false); -test('Prints IPC, verbose custom, fd-specific fd3+stdout', testPrintIpcOrder, 'fd3', 'stdout', false); -test('Prints IPC, verbose custom, fd-specific stdout+ipc', testPrintIpcOrder, 'stdout', 'ipc', false); -test('Prints IPC, verbose custom, fd-specific ipc+stdout', testPrintIpcOrder, 'ipc', 'stdout', true); -test('Prints IPC, verbose custom, fd-specific stderr+fd3', testPrintIpcOrder, 'stderr', 'fd3', false); -test('Prints IPC, verbose custom, fd-specific fd3+stderr', testPrintIpcOrder, 'fd3', 'stderr', false); -test('Prints IPC, verbose custom, fd-specific stderr+ipc', testPrintIpcOrder, 'stderr', 'ipc', false); -test('Prints IPC, verbose custom, fd-specific ipc+stderr', testPrintIpcOrder, 'ipc', 'stderr', true); -test('Prints IPC, verbose custom, fd-specific fd3+ipc', testPrintIpcOrder, 'fd3', 'ipc', false); -test('Prints IPC, verbose custom, fd-specific ipc+fd3', testPrintIpcOrder, 'ipc', 'fd3', true); - -const testPrintIpcFunction = async (t, fdNumber, secondFdNumber, hasOutput) => { - const {stderr} = await runVerboseSubprocess({ - optionsFixture: 'custom-print-function.js', - type: 'ipc', - fdNumber, - secondFdNumber, - ...fullStdio, - }); - - if (hasOutput) { - t.is(getNormalizedLine(stderr), `${testTimestamp} [0] * . .`); - } else { - t.is(stderr, ''); - } -}; - -test('Prints IPC, verbose custom, fd-specific stdout+stderr, single function', testPrintIpcFunction, 'stdout', 'stderr', false); -test('Prints IPC, verbose custom, fd-specific stderr+stdout, single function', testPrintIpcFunction, 'stderr', 'stdout', false); -test('Prints IPC, verbose custom, fd-specific stdout+fd3, single function', testPrintIpcFunction, 'stdout', 'fd3', false); -test('Prints IPC, verbose custom, fd-specific fd3+stdout, single function', testPrintIpcFunction, 'fd3', 'stdout', false); -test('Prints IPC, verbose custom, fd-specific stdout+ipc, single function', testPrintIpcFunction, 'stdout', 'ipc', false); -test('Prints IPC, verbose custom, fd-specific ipc+stdout, single function', testPrintIpcFunction, 'ipc', 'stdout', true); -test('Prints IPC, verbose custom, fd-specific stderr+fd3, single function', testPrintIpcFunction, 'stderr', 'fd3', false); -test('Prints IPC, verbose custom, fd-specific fd3+stderr, single function', testPrintIpcFunction, 'fd3', 'stderr', false); -test('Prints IPC, verbose custom, fd-specific stderr+ipc, single function', testPrintIpcFunction, 'stderr', 'ipc', false); -test('Prints IPC, verbose custom, fd-specific ipc+stderr, single function', testPrintIpcFunction, 'ipc', 'stderr', true); -test('Prints IPC, verbose custom, fd-specific fd3+ipc, single function', testPrintIpcFunction, 'fd3', 'ipc', false); -test('Prints IPC, verbose custom, fd-specific ipc+fd3, single function', testPrintIpcFunction, 'ipc', 'fd3', true); - -test('"verbose" function receives verboseObject.message', async t => { - const {stderr} = await runVerboseSubprocess({ - type: 'ipc', - eventProperty: 'message', - }); - t.is(stderr, '. .'); -}); - -test('"verbose" function receives verboseObject.message line-wise', async t => { - const {stderr} = await runVerboseSubprocess({ - optionsFixture: 'custom-print.js', - type: 'ipc', - output: '.\n.', - }); - t.deepEqual(getNormalizedLines(stderr), [`${testTimestamp} [0] * .`, `${testTimestamp} [0] * .`]); -}); - -test('"verbose" function receives verboseObject.message serialized', async t => { - const {stderr} = await nestedSubprocess('ipc-echo.js', { - ipcInput: foobarObject, - optionsFixture: 'custom-print.js', - optionsInput: {type: 'ipc'}, - }); - t.is(getNormalizedLine(stderr), `${testTimestamp} [0] * ${inspect(foobarObject)}`); -}); diff --git a/test/verbose/custom-options.js b/test/verbose/custom-options.js deleted file mode 100644 index a1d8f53b73..0000000000 --- a/test/verbose/custom-options.js +++ /dev/null @@ -1,47 +0,0 @@ -import test from 'ava'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {runVerboseSubprocess} from '../helpers/verbose.js'; - -setFixtureDirectory(); - -const testVerboseOptionsExplicit = async (t, type, isSync) => { - const maxBuffer = 1000; - const {stderr} = await runVerboseSubprocess({ - isSync, - type, - optionsFixture: 'custom-option.js', - optionName: 'maxBuffer', - maxBuffer, - }); - t.is(stderr, `${maxBuffer}`); -}; - -test('"verbose" function receives verboseObject.options explicitly set, "command"', testVerboseOptionsExplicit, 'command', false); -test('"verbose" function receives verboseObject.options explicitly set, "output"', testVerboseOptionsExplicit, 'output', false); -test('"verbose" function receives verboseObject.options explicitly set, "ipc"', testVerboseOptionsExplicit, 'ipc', false); -test('"verbose" function receives verboseObject.options explicitly set, "error"', testVerboseOptionsExplicit, 'error', false); -test('"verbose" function receives verboseObject.options explicitly set, "duration"', testVerboseOptionsExplicit, 'duration', false); -test('"verbose" function receives verboseObject.options explicitly set, "command", sync', testVerboseOptionsExplicit, 'command', true); -test('"verbose" function receives verboseObject.options explicitly set, "output", sync', testVerboseOptionsExplicit, 'output', true); -test('"verbose" function receives verboseObject.options explicitly set, "error", sync', testVerboseOptionsExplicit, 'error', true); -test('"verbose" function receives verboseObject.options explicitly set, "duration", sync', testVerboseOptionsExplicit, 'duration', true); - -const testVerboseOptionsDefault = async (t, type, isSync) => { - const {stderr} = await runVerboseSubprocess({ - isSync, - type, - optionsFixture: 'custom-option.js', - optionName: 'maxBuffer', - }); - t.is(stderr, 'undefined'); -}; - -test('"verbose" function receives verboseObject.options before default values and normalization, "command"', testVerboseOptionsDefault, 'command', false); -test('"verbose" function receives verboseObject.options before default values and normalization, "output"', testVerboseOptionsDefault, 'output', false); -test('"verbose" function receives verboseObject.options before default values and normalization, "ipc"', testVerboseOptionsDefault, 'ipc', false); -test('"verbose" function receives verboseObject.options before default values and normalization, "error"', testVerboseOptionsDefault, 'error', false); -test('"verbose" function receives verboseObject.options before default values and normalization, "duration"', testVerboseOptionsDefault, 'duration', false); -test('"verbose" function receives verboseObject.options before default values and normalization, "command", sync', testVerboseOptionsDefault, 'command', true); -test('"verbose" function receives verboseObject.options before default values and normalization, "output", sync', testVerboseOptionsDefault, 'output', true); -test('"verbose" function receives verboseObject.options before default values and normalization, "error", sync', testVerboseOptionsDefault, 'error', true); -test('"verbose" function receives verboseObject.options before default values and normalization, "duration", sync', testVerboseOptionsDefault, 'duration', true); diff --git a/test/verbose/custom-output.js b/test/verbose/custom-output.js deleted file mode 100644 index 8f298e33ad..0000000000 --- a/test/verbose/custom-output.js +++ /dev/null @@ -1,128 +0,0 @@ -import {inspect} from 'node:util'; -import test from 'ava'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {fullStdio} from '../helpers/stdio.js'; -import { - getNormalizedLine, - getNormalizedLines, - testTimestamp, - runVerboseSubprocess, -} from '../helpers/verbose.js'; -import {nestedSubprocess} from '../helpers/nested.js'; -import {foobarObject} from '../helpers/input.js'; - -setFixtureDirectory(); - -const testPrintOutputCustom = async (t, fdNumber, isSync, hasOutput) => { - const {stderr} = await runVerboseSubprocess({ - optionsFixture: 'custom-print.js', - isSync, - type: 'output', - fdNumber, - }); - - if (hasOutput) { - t.is(getNormalizedLine(stderr), `${testTimestamp} [0] . .`); - } else { - t.is(stderr, ''); - } -}; - -test('Prints stdout, verbose custom', testPrintOutputCustom, undefined, false, true); -test('Prints stdout, verbose custom, fd-specific stdout', testPrintOutputCustom, 'stdout', false, true); -test('Prints stdout, verbose custom, fd-specific stderr', testPrintOutputCustom, 'stderr', false, false); -test('Prints stdout, verbose custom, fd-specific fd3', testPrintOutputCustom, 'fd3', false, false); -test('Prints stdout, verbose custom, fd-specific ipc', testPrintOutputCustom, 'ipc', false, false); -test('Prints stdout, verbose custom, sync', testPrintOutputCustom, undefined, true, true); -test('Prints stdout, verbose custom, fd-specific stdout, sync', testPrintOutputCustom, 'stdout', true, true); -test('Prints stdout, verbose custom, fd-specific stderr, sync', testPrintOutputCustom, 'stderr', true, false); -test('Prints stdout, verbose custom, fd-specific fd3, sync', testPrintOutputCustom, 'fd3', true, false); -test('Prints stdout, verbose custom, fd-specific ipc, sync', testPrintOutputCustom, 'ipc', true, false); - -const testPrintOutputOrder = async (t, fdNumber, secondFdNumber, hasOutput) => { - const {stderr} = await runVerboseSubprocess({ - optionsFixture: 'custom-print-multiple.js', - type: 'output', - fdNumber, - secondFdNumber, - ...fullStdio, - }); - - if (hasOutput) { - t.is(getNormalizedLine(stderr), `${testTimestamp} [0] . .`); - } else { - t.is(stderr, ''); - } -}; - -test('Prints stdout, verbose custom, fd-specific stdout+stderr', testPrintOutputOrder, 'stdout', 'stderr', true); -test('Prints stdout, verbose custom, fd-specific stderr+stdout', testPrintOutputOrder, 'stderr', 'stdout', false); -test('Prints stdout, verbose custom, fd-specific stdout+fd3', testPrintOutputOrder, 'stdout', 'fd3', true); -test('Prints stdout, verbose custom, fd-specific fd3+stdout', testPrintOutputOrder, 'fd3', 'stdout', false); -test('Prints stdout, verbose custom, fd-specific stdout+ipc', testPrintOutputOrder, 'stdout', 'ipc', true); -test('Prints stdout, verbose custom, fd-specific ipc+stdout', testPrintOutputOrder, 'ipc', 'stdout', false); -test('Prints stdout, verbose custom, fd-specific stderr+fd3', testPrintOutputOrder, 'stderr', 'fd3', false); -test('Prints stdout, verbose custom, fd-specific fd3+stderr', testPrintOutputOrder, 'fd3', 'stderr', false); -test('Prints stdout, verbose custom, fd-specific stderr+ipc', testPrintOutputOrder, 'stderr', 'ipc', false); -test('Prints stdout, verbose custom, fd-specific ipc+stderr', testPrintOutputOrder, 'ipc', 'stderr', false); -test('Prints stdout, verbose custom, fd-specific fd3+ipc', testPrintOutputOrder, 'fd3', 'ipc', false); -test('Prints stdout, verbose custom, fd-specific ipc+fd3', testPrintOutputOrder, 'ipc', 'fd3', false); - -const testPrintOutputFunction = async (t, fdNumber, secondFdNumber, hasOutput) => { - const {stderr} = await runVerboseSubprocess({ - optionsFixture: 'custom-print-function.js', - type: 'output', - fdNumber, - secondFdNumber, - ...fullStdio, - }); - - if (hasOutput) { - t.is(getNormalizedLine(stderr), `${testTimestamp} [0] . .`); - } else { - t.is(stderr, ''); - } -}; - -test('Prints stdout, verbose custom, fd-specific stdout+stderr, single function', testPrintOutputFunction, 'stdout', 'stderr', true); -test('Prints stdout, verbose custom, fd-specific stderr+stdout, single function', testPrintOutputFunction, 'stderr', 'stdout', false); -test('Prints stdout, verbose custom, fd-specific stdout+fd3, single function', testPrintOutputFunction, 'stdout', 'fd3', true); -test('Prints stdout, verbose custom, fd-specific fd3+stdout, single function', testPrintOutputFunction, 'fd3', 'stdout', false); -test('Prints stdout, verbose custom, fd-specific stdout+ipc, single function', testPrintOutputFunction, 'stdout', 'ipc', true); -test('Prints stdout, verbose custom, fd-specific ipc+stdout, single function', testPrintOutputFunction, 'ipc', 'stdout', false); -test('Prints stdout, verbose custom, fd-specific stderr+fd3, single function', testPrintOutputFunction, 'stderr', 'fd3', false); -test('Prints stdout, verbose custom, fd-specific fd3+stderr, single function', testPrintOutputFunction, 'fd3', 'stderr', false); -test('Prints stdout, verbose custom, fd-specific stderr+ipc, single function', testPrintOutputFunction, 'stderr', 'ipc', false); -test('Prints stdout, verbose custom, fd-specific ipc+stderr, single function', testPrintOutputFunction, 'ipc', 'stderr', false); -test('Prints stdout, verbose custom, fd-specific fd3+ipc, single function', testPrintOutputFunction, 'fd3', 'ipc', false); -test('Prints stdout, verbose custom, fd-specific ipc+fd3, single function', testPrintOutputFunction, 'ipc', 'fd3', false); - -const testVerboseMessage = async (t, isSync) => { - const {stderr} = await runVerboseSubprocess({ - isSync, - type: 'output', - eventProperty: 'message', - }); - t.is(stderr, '. .'); -}; - -test('"verbose" function receives verboseObject.message', testVerboseMessage, false); -test('"verbose" function receives verboseObject.message, sync', testVerboseMessage, true); - -const testPrintOutputMultiline = async (t, isSync) => { - const {stderr} = await runVerboseSubprocess({ - optionsFixture: 'custom-print.js', - isSync, - type: 'output', - output: '.\n.', - }); - t.deepEqual(getNormalizedLines(stderr), [`${testTimestamp} [0] .`, `${testTimestamp} [0] .`]); -}; - -test('"verbose" function receives verboseObject.message line-wise', testPrintOutputMultiline, false); -test('"verbose" function receives verboseObject.message line-wise, sync', testPrintOutputMultiline, true); - -test('"verbose" function receives verboseObject.message serialized', async t => { - const {stderr} = await nestedSubprocess('noop.js', {optionsFixture: 'custom-object-stdout.js'}); - t.is(getNormalizedLine(stderr), `${testTimestamp} [0] ${inspect(foobarObject)}`); -}); diff --git a/test/verbose/custom-reject.js b/test/verbose/custom-reject.js deleted file mode 100644 index 1fb25d66e9..0000000000 --- a/test/verbose/custom-reject.js +++ /dev/null @@ -1,37 +0,0 @@ -import test from 'ava'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {runVerboseSubprocess} from '../helpers/verbose.js'; -import {earlyErrorOptions, earlyErrorOptionsSync} from '../helpers/early-error.js'; - -setFixtureDirectory(); - -// eslint-disable-next-line max-params -const testVerboseReject = async (t, type, options, isSync, expectedOutput) => { - const {stderr} = await runVerboseSubprocess({ - isSync, - type, - optionsFixture: 'custom-option.js', - optionName: 'reject', - ...options, - }); - t.is(stderr, expectedOutput.map(String).join('\n')); -}; - -test('"verbose" function receives verboseObject.options.reject, "command"', testVerboseReject, 'command', {}, false, [undefined]); -test('"verbose" function receives verboseObject.options.reject, "output"', testVerboseReject, 'output', {}, false, [undefined]); -test('"verbose" function receives verboseObject.options.reject, "ipc"', testVerboseReject, 'ipc', {}, false, [undefined]); -test('"verbose" function receives verboseObject.options.reject, "error"', testVerboseReject, 'error', {}, false, [undefined]); -test('"verbose" function receives verboseObject.options.reject, "duration"', testVerboseReject, 'duration', {}, false, [undefined]); -test('"verbose" function receives verboseObject.options.reject, "command", spawn error', testVerboseReject, 'command', earlyErrorOptions, false, [undefined]); -test('"verbose" function receives verboseObject.options.reject, "output", spawn error', testVerboseReject, 'output', earlyErrorOptions, false, []); -test('"verbose" function receives verboseObject.options.reject, "ipc", spawn error', testVerboseReject, 'ipc', earlyErrorOptions, false, []); -test('"verbose" function receives verboseObject.options.reject, "error", spawn error', testVerboseReject, 'error', earlyErrorOptions, false, [undefined, undefined]); -test('"verbose" function receives verboseObject.options.reject, "duration", spawn error', testVerboseReject, 'duration', earlyErrorOptions, false, [undefined]); -test('"verbose" function receives verboseObject.options.reject, "command", sync', testVerboseReject, 'command', {}, true, [undefined]); -test('"verbose" function receives verboseObject.options.reject, "output", sync', testVerboseReject, 'output', {}, true, [undefined]); -test('"verbose" function receives verboseObject.options.reject, "error", sync', testVerboseReject, 'error', {}, true, [undefined]); -test('"verbose" function receives verboseObject.options.reject, "duration", sync', testVerboseReject, 'duration', {}, true, [undefined]); -test('"verbose" function receives verboseObject.options.reject, "command", spawn error, sync', testVerboseReject, 'command', earlyErrorOptionsSync, true, [undefined]); -test('"verbose" function receives verboseObject.options.reject, "output", spawn error, sync', testVerboseReject, 'output', earlyErrorOptionsSync, true, []); -test('"verbose" function receives verboseObject.options.reject, "error", spawn error, sync', testVerboseReject, 'error', earlyErrorOptionsSync, true, [undefined, undefined]); -test('"verbose" function receives verboseObject.options.reject, "duration", spawn error, sync', testVerboseReject, 'duration', earlyErrorOptionsSync, true, [undefined]); diff --git a/test/verbose/custom-result.js b/test/verbose/custom-result.js deleted file mode 100644 index 0945ddb58d..0000000000 --- a/test/verbose/custom-result.js +++ /dev/null @@ -1,76 +0,0 @@ -import test from 'ava'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {runVerboseSubprocess} from '../helpers/verbose.js'; -import { - earlyErrorOptions, - earlyErrorOptionsSync, - expectedEarlyError, - expectedEarlyErrorSync, -} from '../helpers/early-error.js'; - -setFixtureDirectory(); - -const testVerboseResultEnd = async (t, type, isSync) => { - const {stderr: parentStderr} = await runVerboseSubprocess({ - isSync, - type, - optionsFixture: 'custom-result.js', - }); - const {failed, exitCode, stdout, stderr, ipcOutput, durationMs} = JSON.parse(parentStderr); - t.true(failed); - t.is(exitCode, 2); - t.is(stdout, '. .'); - t.is(stderr, ''); - t.is(typeof durationMs, 'number'); - t.deepEqual(ipcOutput, isSync ? [] : ['. .']); -}; - -test('"verbose" function receives verboseObject.result, "error"', testVerboseResultEnd, 'error', false); -test('"verbose" function receives verboseObject.result, "duration"', testVerboseResultEnd, 'duration', false); -test('"verbose" function receives verboseObject.result, "error", sync', testVerboseResultEnd, 'error', true); -test('"verbose" function receives verboseObject.result, "duration", sync', testVerboseResultEnd, 'duration', true); - -// eslint-disable-next-line max-params -const testVerboseResultEndSpawn = async (t, type, options, expectedOutput, isSync) => { - const {stderr: parentStderr} = await runVerboseSubprocess({ - isSync, - type, - optionsFixture: 'custom-result.js', - ...options, - }); - const lastLine = parentStderr.split('\n').at(-1); - const result = JSON.parse(lastLine); - t.like(result, expectedOutput); - t.true(result.failed); - t.is(result.exitCode, undefined); - t.is(result.stdout, undefined); - t.is(result.stderr, undefined); - t.is(typeof result.durationMs, 'number'); - t.deepEqual(result.ipcOutput, []); -}; - -test('"verbose" function receives verboseObject.result, "error", spawn error', testVerboseResultEndSpawn, 'error', earlyErrorOptions, expectedEarlyError, false); -test('"verbose" function receives verboseObject.result, "duration", spawn error', testVerboseResultEndSpawn, 'duration', earlyErrorOptions, expectedEarlyError, false); -test('"verbose" function receives verboseObject.result, "error", spawn error, sync', testVerboseResultEndSpawn, 'error', earlyErrorOptionsSync, expectedEarlyErrorSync, true); -test('"verbose" function receives verboseObject.result, "duration", spawn error, sync', testVerboseResultEndSpawn, 'duration', earlyErrorOptionsSync, expectedEarlyErrorSync, true); - -const testVerboseResultStart = async (t, type, options, isSync) => { - const {stderr: parentStderr} = await runVerboseSubprocess({ - isSync, - type, - optionsFixture: 'custom-result.js', - ...options, - }); - t.is(parentStderr, ''); -}; - -test('"verbose" function does not receive verboseObject.result, "command"', testVerboseResultStart, 'command', {}, false); -test('"verbose" function does not receive verboseObject.result, "output"', testVerboseResultStart, 'output', {}, false); -test('"verbose" function does not receive verboseObject.result, "ipc"', testVerboseResultStart, 'ipc', {}, false); -test('"verbose" function does not receive verboseObject.result, "command", spawn error', testVerboseResultStart, 'command', earlyErrorOptions, false); -test('"verbose" function does not receive verboseObject.result, "output", spawn error', testVerboseResultStart, 'output', earlyErrorOptions, false); -test('"verbose" function does not receive verboseObject.result, "ipc", spawn error', testVerboseResultStart, 'ipc', earlyErrorOptions, false); -test('"verbose" function does not receive verboseObject.result, "command", sync', testVerboseResultStart, 'command', {}, true); -test('"verbose" function does not receive verboseObject.result, "output", sync', testVerboseResultStart, 'output', {}, true); -test('"verbose" function does not receive verboseObject.result, "command", spawn error, sync', testVerboseResultStart, 'command', earlyErrorOptionsSync, true); -test('"verbose" function does not receive verboseObject.result, "output", spawn error, sync', testVerboseResultStart, 'output', earlyErrorOptionsSync, true); diff --git a/test/verbose/custom-start.js b/test/verbose/custom-start.js deleted file mode 100644 index cb7904e043..0000000000 --- a/test/verbose/custom-start.js +++ /dev/null @@ -1,84 +0,0 @@ -import test from 'ava'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {fullStdio} from '../helpers/stdio.js'; -import { - QUOTE, - getNormalizedLine, - testTimestamp, - runVerboseSubprocess, -} from '../helpers/verbose.js'; - -setFixtureDirectory(); - -const testPrintCommandCustom = async (t, fdNumber, worker, isSync) => { - const {stderr} = await runVerboseSubprocess({ - optionsFixture: 'custom-print.js', - worker, - isSync, - type: 'command', - fdNumber, - }); - t.is(getNormalizedLine(stderr), `${testTimestamp} [0] $ noop-verbose.js ${QUOTE}. .${QUOTE}`); -}; - -test('Prints command, verbose custom', testPrintCommandCustom, undefined, false, false); -test('Prints command, verbose custom, fd-specific stdout', testPrintCommandCustom, 'stdout', false, false); -test('Prints command, verbose custom, fd-specific stderr', testPrintCommandCustom, 'stderr', false, false); -test('Prints command, verbose custom, fd-specific fd3', testPrintCommandCustom, 'fd3', false, false); -test('Prints command, verbose custom, fd-specific ipc', testPrintCommandCustom, 'ipc', false, false); -test('Prints command, verbose custom, sync', testPrintCommandCustom, undefined, false, true); -test('Prints command, verbose custom, fd-specific stdout, sync', testPrintCommandCustom, 'stdout', false, true); -test('Prints command, verbose custom, fd-specific stderr, sync', testPrintCommandCustom, 'stderr', false, true); -test('Prints command, verbose custom, fd-specific fd3, sync', testPrintCommandCustom, 'fd3', false, true); -test('Prints command, verbose custom, fd-specific ipc, sync', testPrintCommandCustom, 'ipc', false, true); -test('Prints command, verbose custom, worker', testPrintCommandCustom, undefined, true, false); -test('Prints command, verbose custom, fd-specific stdout, worker', testPrintCommandCustom, 'stdout', true, false); -test('Prints command, verbose custom, fd-specific stderr, worker', testPrintCommandCustom, 'stderr', true, false); -test('Prints command, verbose custom, fd-specific fd3, worker', testPrintCommandCustom, 'fd3', true, false); -test('Prints command, verbose custom, fd-specific ipc, worker', testPrintCommandCustom, 'ipc', true, false); -test('Prints command, verbose custom, worker, sync', testPrintCommandCustom, undefined, true, true); -test('Prints command, verbose custom, fd-specific stdout, worker, sync', testPrintCommandCustom, 'stdout', true, true); -test('Prints command, verbose custom, fd-specific stderr, worker, sync', testPrintCommandCustom, 'stderr', true, true); -test('Prints command, verbose custom, fd-specific fd3, worker, sync', testPrintCommandCustom, 'fd3', true, true); -test('Prints command, verbose custom, fd-specific ipc, worker, sync', testPrintCommandCustom, 'ipc', true, true); - -const testPrintCommandOrder = async (t, fdNumber, secondFdNumber, hasOutput) => { - const {stderr} = await runVerboseSubprocess({ - optionsFixture: 'custom-print-multiple.js', - type: 'command', - fdNumber, - secondFdNumber, - ...fullStdio, - }); - - if (hasOutput) { - t.is(getNormalizedLine(stderr), `${testTimestamp} [0] $ noop-verbose.js ${QUOTE}. .${QUOTE}`); - } else { - t.is(stderr, ''); - } -}; - -test('Prints command, verbose custom, fd-specific stdout+stderr', testPrintCommandOrder, 'stdout', 'stderr', true); -test('Prints command, verbose custom, fd-specific stderr+stdout', testPrintCommandOrder, 'stderr', 'stdout', false); -test('Prints command, verbose custom, fd-specific stdout+fd3', testPrintCommandOrder, 'stdout', 'fd3', true); -test('Prints command, verbose custom, fd-specific fd3+stdout', testPrintCommandOrder, 'fd3', 'stdout', false); -test('Prints command, verbose custom, fd-specific stdout+ipc', testPrintCommandOrder, 'stdout', 'ipc', true); -test('Prints command, verbose custom, fd-specific ipc+stdout', testPrintCommandOrder, 'ipc', 'stdout', false); -test('Prints command, verbose custom, fd-specific stderr+fd3', testPrintCommandOrder, 'stderr', 'fd3', true); -test('Prints command, verbose custom, fd-specific fd3+stderr', testPrintCommandOrder, 'fd3', 'stderr', false); -test('Prints command, verbose custom, fd-specific stderr+ipc', testPrintCommandOrder, 'stderr', 'ipc', true); -test('Prints command, verbose custom, fd-specific ipc+stderr', testPrintCommandOrder, 'ipc', 'stderr', false); -test('Prints command, verbose custom, fd-specific fd3+ipc', testPrintCommandOrder, 'fd3', 'ipc', true); -test('Prints command, verbose custom, fd-specific ipc+fd3', testPrintCommandOrder, 'ipc', 'fd3', false); - -const testVerboseMessage = async (t, isSync) => { - const {stderr} = await runVerboseSubprocess({ - isSync, - type: 'command', - eventProperty: 'message', - }); - t.is(stderr, `noop-verbose.js ${QUOTE}. .${QUOTE}`); -}; - -test('"verbose" function receives verboseObject.message', testVerboseMessage, false); -test('"verbose" function receives verboseObject.message, sync', testVerboseMessage, true); diff --git a/test/verbose/custom-throw.js b/test/verbose/custom-throw.js deleted file mode 100644 index afeab7ba03..0000000000 --- a/test/verbose/custom-throw.js +++ /dev/null @@ -1,67 +0,0 @@ -import test from 'ava'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString} from '../helpers/input.js'; -import {runVerboseSubprocess} from '../helpers/verbose.js'; -import {earlyErrorOptions, earlyErrorOptionsSync} from '../helpers/early-error.js'; - -setFixtureDirectory(); - -const testCommandThrowPropagate = async (t, type, options, isSync) => { - const {nestedResult} = await runVerboseSubprocess({ - isSync, - type, - optionsFixture: 'custom-throw.js', - errorMessage: foobarString, - ...options, - }); - t.true(nestedResult instanceof Error); - t.is(nestedResult.message, foobarString); -}; - -test('Propagate verbose exception in "verbose" function, "command"', testCommandThrowPropagate, 'command', {}, false); -test('Propagate verbose exception in "verbose" function, "error"', testCommandThrowPropagate, 'error', {}, false); -test('Propagate verbose exception in "verbose" function, "duration"', testCommandThrowPropagate, 'duration', {}, false); -test('Propagate verbose exception in "verbose" function, "command", spawn error', testCommandThrowPropagate, 'command', earlyErrorOptions, false); -test('Propagate verbose exception in "verbose" function, "error", spawn error', testCommandThrowPropagate, 'error', earlyErrorOptions, false); -test('Propagate verbose exception in "verbose" function, "duration", spawn error', testCommandThrowPropagate, 'duration', earlyErrorOptions, false); -test('Propagate verbose exception in "verbose" function, "command", sync', testCommandThrowPropagate, 'command', {}, true); -test('Propagate verbose exception in "verbose" function, "error", sync', testCommandThrowPropagate, 'error', {}, true); -test('Propagate verbose exception in "verbose" function, "duration", sync', testCommandThrowPropagate, 'duration', {}, true); -test('Propagate verbose exception in "verbose" function, "command", spawn error, sync', testCommandThrowPropagate, 'command', earlyErrorOptionsSync, true); -test('Propagate verbose exception in "verbose" function, "error", spawn error, sync', testCommandThrowPropagate, 'error', earlyErrorOptionsSync, true); -test('Propagate verbose exception in "verbose" function, "duration", spawn error, sync', testCommandThrowPropagate, 'duration', earlyErrorOptionsSync, true); - -const testCommandThrowHandle = async (t, type, isSync) => { - const {nestedResult} = await runVerboseSubprocess({ - isSync, - type, - optionsFixture: 'custom-throw.js', - errorMessage: foobarString, - }); - t.true(nestedResult instanceof Error); - t.true(nestedResult.stack.startsWith(isSync ? 'ExecaSyncError' : 'ExecaError')); - t.true(nestedResult.cause instanceof Error); - t.is(nestedResult.cause.message, foobarString); -}; - -test('Handle exceptions in "verbose" function, "output"', testCommandThrowHandle, 'output', false); -test('Handle exceptions in "verbose" function, "ipc"', testCommandThrowHandle, 'ipc', false); -test('Handle exceptions in "verbose" function, "output", sync', testCommandThrowHandle, 'output', true); - -const testCommandThrowWrap = async (t, type, options, isSync) => { - const {nestedResult} = await runVerboseSubprocess({ - isSync, - type, - optionsFixture: 'custom-throw.js', - errorMessage: foobarString, - ...options, - }); - t.true(nestedResult instanceof Error); - t.true(nestedResult.stack.startsWith(isSync ? 'ExecaSyncError' : 'ExecaError')); - t.true(nestedResult.cause instanceof Error); - t.not(nestedResult.cause.message, foobarString); -}; - -test('Propagate wrapped exception in "verbose" function, "output", spawn error', testCommandThrowWrap, 'output', earlyErrorOptions, false); -test('Propagate wrapped exception in "verbose" function, "ipc", spawn error', testCommandThrowWrap, 'ipc', earlyErrorOptions, false); -test('Propagate wrapped exception in "verbose" function, "output", spawn error, sync', testCommandThrowWrap, 'output', earlyErrorOptionsSync, true); diff --git a/test/verbose/error.js b/test/verbose/error.js deleted file mode 100644 index 4b018d984d..0000000000 --- a/test/verbose/error.js +++ /dev/null @@ -1,166 +0,0 @@ -import test from 'ava'; -import {red} from 'yoctocolors'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString} from '../helpers/input.js'; -import {nestedSubprocess} from '../helpers/nested.js'; -import { - QUOTE, - runErrorSubprocess, - runEarlyErrorSubprocess, - getErrorLine, - getErrorLines, - testTimestamp, - getVerboseOption, - stdoutNoneOption, - stdoutShortOption, - stdoutFullOption, - stderrNoneOption, - stderrShortOption, - stderrFullOption, - fd3NoneOption, - fd3ShortOption, - fd3FullOption, - ipcNoneOption, - ipcShortOption, - ipcFullOption, -} from '../helpers/verbose.js'; - -setFixtureDirectory(); - -const testPrintError = async (t, verbose, isSync) => { - const stderr = await runErrorSubprocess(t, verbose, isSync); - t.is(getErrorLine(stderr), `${testTimestamp} [0] × Command failed with exit code 2: noop-fail.js 1 ${foobarString}`); -}; - -test('Prints error, verbose "short"', testPrintError, 'short', false); -test('Prints error, verbose "full"', testPrintError, 'full', false); -test('Prints error, verbose "short", fd-specific stdout', testPrintError, stdoutShortOption, false); -test('Prints error, verbose "full", fd-specific stdout', testPrintError, stdoutFullOption, false); -test('Prints error, verbose "short", fd-specific stderr', testPrintError, stderrShortOption, false); -test('Prints error, verbose "full", fd-specific stderr', testPrintError, stderrFullOption, false); -test('Prints error, verbose "short", fd-specific fd3', testPrintError, fd3ShortOption, false); -test('Prints error, verbose "full", fd-specific fd3', testPrintError, fd3FullOption, false); -test('Prints error, verbose "short", fd-specific ipc', testPrintError, ipcShortOption, false); -test('Prints error, verbose "full", fd-specific ipc', testPrintError, ipcFullOption, false); -test('Prints error, verbose "short", sync', testPrintError, 'short', true); -test('Prints error, verbose "full", sync', testPrintError, 'full', true); -test('Prints error, verbose "short", fd-specific stdout, sync', testPrintError, stdoutShortOption, true); -test('Prints error, verbose "full", fd-specific stdout, sync', testPrintError, stdoutFullOption, true); -test('Prints error, verbose "short", fd-specific stderr, sync', testPrintError, stderrShortOption, true); -test('Prints error, verbose "full", fd-specific stderr, sync', testPrintError, stderrFullOption, true); -test('Prints error, verbose "short", fd-specific fd3, sync', testPrintError, fd3ShortOption, true); -test('Prints error, verbose "full", fd-specific fd3, sync', testPrintError, fd3FullOption, true); -test('Prints error, verbose "short", fd-specific ipc, sync', testPrintError, ipcShortOption, true); -test('Prints error, verbose "full", fd-specific ipc, sync', testPrintError, ipcFullOption, true); - -const testNoPrintError = async (t, verbose, isSync) => { - const stderr = await runErrorSubprocess(t, verbose, isSync, false); - t.is(getErrorLine(stderr), undefined); -}; - -test('Does not print error, verbose "none"', testNoPrintError, 'none', false); -test('Does not print error, verbose default', testNoPrintError, undefined, false); -test('Does not print error, verbose "none", fd-specific stdout', testNoPrintError, stdoutNoneOption, false); -test('Does not print error, verbose "none", fd-specific stderr', testNoPrintError, stderrNoneOption, false); -test('Does not print error, verbose "none", fd-specific fd3', testNoPrintError, fd3NoneOption, false); -test('Does not print error, verbose "none", fd-specific ipc', testNoPrintError, ipcNoneOption, false); -test('Does not print error, verbose default, fd-specific', testNoPrintError, {}, false); -test('Does not print error, verbose "none", sync', testNoPrintError, 'none', true); -test('Does not print error, verbose default, sync', testNoPrintError, undefined, true); -test('Does not print error, verbose "none", fd-specific stdout, sync', testNoPrintError, stdoutNoneOption, true); -test('Does not print error, verbose "none", fd-specific stderr, sync', testNoPrintError, stderrNoneOption, true); -test('Does not print error, verbose "none", fd-specific fd3, sync', testNoPrintError, fd3NoneOption, true); -test('Does not print error, verbose "none", fd-specific ipc, sync', testNoPrintError, ipcNoneOption, true); -test('Does not print error, verbose default, fd-specific, sync', testNoPrintError, {}, true); - -const testPrintNoError = async (t, isSync) => { - const {stderr} = await nestedSubprocess('noop.js', [foobarString], {verbose: 'short', isSync}); - t.is(getErrorLine(stderr), undefined); -}; - -test('Does not print error if none', testPrintNoError, false); -test('Does not print error if none, sync', testPrintNoError, true); - -const testPrintErrorEarly = async (t, isSync) => { - const stderr = await runEarlyErrorSubprocess(t, isSync); - t.is(getErrorLine(stderr), undefined); -}; - -test('Prints early validation error', testPrintErrorEarly, false); -test('Prints early validation error, sync', testPrintErrorEarly, true); - -test('Does not repeat stdout|stderr with error', async t => { - const stderr = await runErrorSubprocess(t, 'short'); - t.deepEqual(getErrorLines(stderr), [`${testTimestamp} [0] × Command failed with exit code 2: noop-fail.js 1 ${foobarString}`]); -}); - -test('Prints error differently if "reject" is false', async t => { - const {stderr} = await nestedSubprocess('noop-fail.js', ['1', foobarString], {verbose: 'short', reject: false}); - t.deepEqual(getErrorLines(stderr), [`${testTimestamp} [0] ‼ Command failed with exit code 2: noop-fail.js 1 ${foobarString}`]); -}); - -const testPipeError = async (t, parentFixture, sourceVerbose, destinationVerbose) => { - const {stderr} = await t.throwsAsync(nestedSubprocess('noop-fail.js', ['1'], { - parentFixture, - sourceOptions: getVerboseOption(sourceVerbose), - destinationFile: 'stdin-fail.js', - destinationOptions: getVerboseOption(destinationVerbose), - })); - - const lines = getErrorLines(stderr); - t.is(lines.includes(`${testTimestamp} [0] × Command failed with exit code 2: noop-fail.js 1`), sourceVerbose); - t.is(lines.includes(`${testTimestamp} [${sourceVerbose ? 1 : 0}] × Command failed with exit code 2: stdin-fail.js`), destinationVerbose); -}; - -test('Prints both errors piped with .pipe("file")', testPipeError, 'nested-pipe-file.js', true, true); -test('Prints both errors piped with .pipe`command`', testPipeError, 'nested-pipe-script.js', true, true); -test('Prints both errors piped with .pipe(subprocess)', testPipeError, 'nested-pipe-subprocesses.js', true, true); -test('Prints first error piped with .pipe("file")', testPipeError, 'nested-pipe-file.js', true, false); -test('Prints first error piped with .pipe`command`', testPipeError, 'nested-pipe-script.js', true, false); -test('Prints first error piped with .pipe(subprocess)', testPipeError, 'nested-pipe-subprocesses.js', true, false); -test('Prints second error piped with .pipe("file")', testPipeError, 'nested-pipe-file.js', false, true); -test('Prints second error piped with .pipe`command`', testPipeError, 'nested-pipe-script.js', false, true); -test('Prints second error piped with .pipe(subprocess)', testPipeError, 'nested-pipe-subprocesses.js', false, true); -test('Prints neither errors piped with .pipe("file")', testPipeError, 'nested-pipe-file.js', false, false); -test('Prints neither errors piped with .pipe`command`', testPipeError, 'nested-pipe-script.js', false, false); -test('Prints neither errors piped with .pipe(subprocess)', testPipeError, 'nested-pipe-subprocesses.js', false, false); - -test('Quotes spaces from error', async t => { - const {stderr} = await t.throwsAsync(nestedSubprocess('noop-forever.js', ['foo bar'], {parentFixture: 'nested-fail.js', verbose: 'short'})); - t.deepEqual(getErrorLines(stderr), [ - `${testTimestamp} [0] × Command was killed with SIGTERM (Termination): noop-forever.js ${QUOTE}foo bar${QUOTE}`, - `${testTimestamp} [0] × foo bar`, - ]); -}); - -test('Quotes special punctuation from error', async t => { - const {stderr} = await t.throwsAsync(nestedSubprocess('noop-forever.js', ['%'], {parentFixture: 'nested-fail.js', verbose: 'short'})); - t.deepEqual(getErrorLines(stderr), [ - `${testTimestamp} [0] × Command was killed with SIGTERM (Termination): noop-forever.js ${QUOTE}%${QUOTE}`, - `${testTimestamp} [0] × %`, - ]); -}); - -test('Does not escape internal characters from error', async t => { - const {stderr} = await t.throwsAsync(nestedSubprocess('noop-forever.js', ['ã'], {parentFixture: 'nested-fail.js', verbose: 'short'})); - t.deepEqual(getErrorLines(stderr), [ - `${testTimestamp} [0] × Command was killed with SIGTERM (Termination): noop-forever.js ${QUOTE}ã${QUOTE}`, - `${testTimestamp} [0] × ã`, - ]); -}); - -test('Escapes and strips color sequences from error', async t => { - const {stderr} = await t.throwsAsync(nestedSubprocess('noop-forever.js', [red(foobarString)], {parentFixture: 'nested-fail.js', verbose: 'short'}, {env: {FORCE_COLOR: '1'}})); - t.deepEqual(getErrorLines(stderr), [ - `${testTimestamp} [0] × Command was killed with SIGTERM (Termination): noop-forever.js ${QUOTE}\\u001b[31m${foobarString}\\u001b[39m${QUOTE}`, - `${testTimestamp} [0] × ${foobarString}`, - ]); -}); - -test('Escapes control characters from error', async t => { - const {stderr} = await t.throwsAsync(nestedSubprocess('noop-forever.js', ['\u0001'], {parentFixture: 'nested-fail.js', verbose: 'short'})); - t.deepEqual(getErrorLines(stderr), [ - `${testTimestamp} [0] × Command was killed with SIGTERM (Termination): noop-forever.js ${QUOTE}\\u0001${QUOTE}`, - `${testTimestamp} [0] × \\u0001`, - ]); -}); diff --git a/test/verbose/info.js b/test/verbose/info.js deleted file mode 100644 index 5c9188f28c..0000000000 --- a/test/verbose/info.js +++ /dev/null @@ -1,92 +0,0 @@ -import test from 'ava'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {execa, execaSync} from '../../index.js'; -import {foobarString} from '../helpers/input.js'; -import {nestedSubprocess} from '../helpers/nested.js'; -import { - QUOTE, - getCommandLine, - getOutputLine, - getNormalizedLines, - testTimestamp, -} from '../helpers/verbose.js'; -import {earlyErrorOptions, earlyErrorOptionsSync} from '../helpers/early-error.js'; - -setFixtureDirectory(); - -const testVerboseGeneral = async (t, execaMethod) => { - const {all} = await execaMethod('verbose-script.js', {env: {NODE_DEBUG: 'execa'}, all: true}); - t.deepEqual(getNormalizedLines(all), [ - `${testTimestamp} [0] $ node -e ${QUOTE}console.error(1)${QUOTE}`, - '1', - `${testTimestamp} [0] √ (done in 0ms)`, - `${testTimestamp} [1] $ node -e ${QUOTE}process.exit(2)${QUOTE}`, - `${testTimestamp} [1] ‼ Command failed with exit code 2: node -e ${QUOTE}process.exit(2)${QUOTE}`, - `${testTimestamp} [1] ‼ (done in 0ms)`, - ]); -}; - -test('Prints command, NODE_DEBUG=execa + "inherit"', testVerboseGeneral, execa); -test('Prints command, NODE_DEBUG=execa + "inherit", sync', testVerboseGeneral, execaSync); - -test('NODE_DEBUG=execa changes verbose default value to "full"', async t => { - const {stderr} = await nestedSubprocess('noop.js', [foobarString], {}, {env: {NODE_DEBUG: 'execa'}}); - t.is(getCommandLine(stderr), `${testTimestamp} [0] $ noop.js ${foobarString}`); - t.is(getOutputLine(stderr), `${testTimestamp} [0] ${foobarString}`); -}); - -const testDebugEnvPriority = async (t, isSync) => { - const {stderr} = await nestedSubprocess('noop.js', [foobarString], {verbose: 'short', isSync}, {env: {NODE_DEBUG: 'execa'}}); - t.is(getCommandLine(stderr), `${testTimestamp} [0] $ noop.js ${foobarString}`); - t.is(getOutputLine(stderr), undefined); -}; - -test('NODE_DEBUG=execa has lower priority', testDebugEnvPriority, false); -test('NODE_DEBUG=execa has lower priority, sync', testDebugEnvPriority, true); - -const invalidFalseMessage = 'renamed to "verbose: \'none\'"'; -const invalidTrueMessage = 'renamed to "verbose: \'short\'"'; -const invalidUnknownMessage = 'Allowed values are: \'none\', \'short\', \'full\''; - -const testInvalidVerbose = (t, verbose, expectedMessage, execaMethod) => { - const {message} = t.throws(() => { - execaMethod('empty.js', {verbose}); - }); - t.true(message.includes(expectedMessage)); -}; - -test('Does not allow "verbose: false"', testInvalidVerbose, false, invalidFalseMessage, execa); -test('Does not allow "verbose: false", sync', testInvalidVerbose, false, invalidFalseMessage, execaSync); -test('Does not allow "verbose: true"', testInvalidVerbose, true, invalidTrueMessage, execa); -test('Does not allow "verbose: true", sync', testInvalidVerbose, true, invalidTrueMessage, execaSync); -test('Does not allow "verbose: \'unknown\'"', testInvalidVerbose, 'unknown', invalidUnknownMessage, execa); -test('Does not allow "verbose: \'unknown\'", sync', testInvalidVerbose, 'unknown', invalidUnknownMessage, execaSync); - -const testValidationError = async (t, isSync) => { - const {stderr, nestedResult} = await nestedSubprocess('empty.js', {verbose: 'full', isSync, timeout: []}); - t.deepEqual(getNormalizedLines(stderr), [`${testTimestamp} [0] $ empty.js`]); - t.true(nestedResult instanceof Error); -}; - -test('Prints validation errors', testValidationError, false); -test('Prints validation errors, sync', testValidationError, true); - -test('Prints early spawn errors', async t => { - const {stderr} = await nestedSubprocess('empty.js', {...earlyErrorOptions, verbose: 'full'}); - t.deepEqual(getNormalizedLines(stderr), [ - `${testTimestamp} [0] $ empty.js`, - `${testTimestamp} [0] × Command failed with ERR_INVALID_ARG_TYPE: empty.js`, - `${testTimestamp} [0] × The "options.detached" property must be of type boolean. Received type string ('true')`, - `${testTimestamp} [0] × (done in 0ms)`, - ]); -}); - -test('Prints early spawn errors, sync', async t => { - const {stderr} = await nestedSubprocess('empty.js', {...earlyErrorOptionsSync, verbose: 'full', isSync: true}); - t.deepEqual(getNormalizedLines(stderr), [ - `${testTimestamp} [0] $ empty.js`, - `${testTimestamp} [0] × Command failed with ERR_OUT_OF_RANGE: empty.js`, - `${testTimestamp} [0] × The value of "options.maxBuffer" is out of range. It must be a positive number. Received false`, - `${testTimestamp} [0] × (done in 0ms)`, - ]); -}); diff --git a/test/verbose/ipc.js b/test/verbose/ipc.js deleted file mode 100644 index b842746df4..0000000000 --- a/test/verbose/ipc.js +++ /dev/null @@ -1,108 +0,0 @@ -import {on} from 'node:events'; -import {inspect} from 'node:util'; -import test from 'ava'; -import {red} from 'yoctocolors'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString, foobarObject} from '../helpers/input.js'; -import {nestedSubprocess, nestedInstance} from '../helpers/nested.js'; -import { - getIpcLine, - getIpcLines, - testTimestamp, - ipcNoneOption, - ipcShortOption, - ipcFullOption, -} from '../helpers/verbose.js'; - -setFixtureDirectory(); - -const testPrintIpc = async (t, verbose) => { - const {stderr} = await nestedSubprocess('ipc-send.js', {ipc: true, verbose}); - t.is(getIpcLine(stderr), `${testTimestamp} [0] * ${foobarString}`); -}; - -test('Prints IPC, verbose "full"', testPrintIpc, 'full'); -test('Prints IPC, verbose "full", fd-specific', testPrintIpc, ipcFullOption); - -const testNoPrintIpc = async (t, verbose) => { - const {stderr} = await nestedSubprocess('ipc-send.js', {ipc: true, verbose}); - t.is(getIpcLine(stderr), undefined); -}; - -test('Does not print IPC, verbose default', testNoPrintIpc, undefined); -test('Does not print IPC, verbose "none"', testNoPrintIpc, 'none'); -test('Does not print IPC, verbose "short"', testNoPrintIpc, 'short'); -test('Does not print IPC, verbose default, fd-specific', testNoPrintIpc, {}); -test('Does not print IPC, verbose "none", fd-specific', testNoPrintIpc, ipcNoneOption); -test('Does not print IPC, verbose "short", fd-specific', testNoPrintIpc, ipcShortOption); - -const testNoIpc = async (t, ipc) => { - const {nestedResult, stderr} = await nestedSubprocess('ipc-send.js', {ipc, verbose: 'full'}); - t.true(nestedResult instanceof Error); - t.true(nestedResult.message.includes('sendMessage() can only be used')); - t.is(getIpcLine(stderr), undefined); -}; - -test('Does not print IPC, ipc: false', testNoIpc, false); -test('Does not print IPC, ipc: default', testNoIpc, undefined); - -test('Prints objects from IPC', async t => { - const {stderr} = await nestedSubprocess('ipc-send-json.js', [JSON.stringify(foobarObject)], {ipc: true, verbose: 'full'}); - t.is(getIpcLine(stderr), `${testTimestamp} [0] * ${inspect(foobarObject)}`); -}); - -test('Prints multiline arrays from IPC', async t => { - const bigArray = Array.from({length: 100}, (_, index) => index); - const {stderr} = await nestedSubprocess('ipc-send-json.js', [JSON.stringify(bigArray)], {ipc: true, verbose: 'full'}); - const ipcLines = getIpcLines(stderr); - t.is(ipcLines[0], `${testTimestamp} [0] * [`); - t.is(ipcLines.at(-2), `${testTimestamp} [0] * 96, 97, 98, 99`); - t.is(ipcLines.at(-1), `${testTimestamp} [0] * ]`); -}); - -test('Does not quote spaces from IPC', async t => { - const {stderr} = await nestedSubprocess('ipc-send.js', ['foo bar'], {ipc: true, verbose: 'full'}); - t.is(getIpcLine(stderr), `${testTimestamp} [0] * foo bar`); -}); - -test('Does not quote newlines from IPC', async t => { - const {stderr} = await nestedSubprocess('ipc-send.js', ['foo\nbar'], {ipc: true, verbose: 'full'}); - t.deepEqual(getIpcLines(stderr), [ - `${testTimestamp} [0] * foo`, - `${testTimestamp} [0] * bar`, - ]); -}); - -test('Does not quote special punctuation from IPC', async t => { - const {stderr} = await nestedSubprocess('ipc-send.js', ['%'], {ipc: true, verbose: 'full'}); - t.is(getIpcLine(stderr), `${testTimestamp} [0] * %`); -}); - -test('Does not escape internal characters from IPC', async t => { - const {stderr} = await nestedSubprocess('ipc-send.js', ['ã'], {ipc: true, verbose: 'full'}); - t.is(getIpcLine(stderr), `${testTimestamp} [0] * ã`); -}); - -test('Strips color sequences from IPC', async t => { - const {stderr} = await nestedSubprocess('ipc-send.js', [red(foobarString)], {ipc: true, verbose: 'full'}, {env: {FORCE_COLOR: '1'}}); - t.is(getIpcLine(stderr), `${testTimestamp} [0] * ${foobarString}`); -}); - -test('Escapes control characters from IPC', async t => { - const {stderr} = await nestedSubprocess('ipc-send.js', ['\u0001'], {ipc: true, verbose: 'full'}); - t.is(getIpcLine(stderr), `${testTimestamp} [0] * \\u0001`); -}); - -test('Prints IPC progressively', async t => { - const subprocess = nestedInstance('ipc-send-forever.js', {ipc: true, verbose: 'full'}); - for await (const chunk of on(subprocess.stderr, 'data')) { - const ipcLine = getIpcLine(chunk.toString()); - if (ipcLine !== undefined) { - t.is(ipcLine, `${testTimestamp} [0] * ${foobarString}`); - break; - } - } - - subprocess.kill(); - await t.throwsAsync(subprocess); -}); diff --git a/test/verbose/log.js b/test/verbose/log.js deleted file mode 100644 index c54d012550..0000000000 --- a/test/verbose/log.js +++ /dev/null @@ -1,27 +0,0 @@ -import {stripVTControlCharacters} from 'node:util'; -import test from 'ava'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString} from '../helpers/input.js'; -import {nestedSubprocess} from '../helpers/nested.js'; - -setFixtureDirectory(); - -const testNoStdout = async (t, verbose, isSync) => { - const {stdout} = await nestedSubprocess('noop.js', [foobarString], {verbose, stdio: 'inherit', isSync}); - t.is(stdout, foobarString); -}; - -test('Logs on stderr not stdout, verbose "none"', testNoStdout, 'none', false); -test('Logs on stderr not stdout, verbose "short"', testNoStdout, 'short', false); -test('Logs on stderr not stdout, verbose "full"', testNoStdout, 'full', false); -test('Logs on stderr not stdout, verbose "none", sync', testNoStdout, 'none', true); -test('Logs on stderr not stdout, verbose "short", sync', testNoStdout, 'short', true); -test('Logs on stderr not stdout, verbose "full", sync', testNoStdout, 'full', true); - -const testColor = async (t, expectedResult, forceColor) => { - const {stderr} = await nestedSubprocess('noop.js', [foobarString], {verbose: 'short'}, {env: {FORCE_COLOR: forceColor}}); - t.is(stderr !== stripVTControlCharacters(stderr), expectedResult); -}; - -test('Prints with colors if supported', testColor, true, '1'); -test('Prints without colors if not supported', testColor, false, '0'); diff --git a/test/verbose/output-buffer.js b/test/verbose/output-buffer.js deleted file mode 100644 index 250a3efb11..0000000000 --- a/test/verbose/output-buffer.js +++ /dev/null @@ -1,52 +0,0 @@ -import test from 'ava'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString, foobarUppercase} from '../helpers/input.js'; -import {nestedSubprocess} from '../helpers/nested.js'; -import { - getOutputLine, - testTimestamp, - stdoutNoneOption, - stdoutFullOption, - stderrFullOption, -} from '../helpers/verbose.js'; - -setFixtureDirectory(); - -const testPrintOutputNoBuffer = async (t, verbose, buffer, isSync) => { - const {stderr} = await nestedSubprocess('noop.js', [foobarString], {verbose, buffer, isSync}); - t.is(getOutputLine(stderr), `${testTimestamp} [0] ${foobarString}`); -}; - -test('Prints stdout, buffer: false', testPrintOutputNoBuffer, 'full', false, false); -test('Prints stdout, buffer: false, fd-specific buffer', testPrintOutputNoBuffer, 'full', {stdout: false}, false); -test('Prints stdout, buffer: false, fd-specific verbose', testPrintOutputNoBuffer, stdoutFullOption, false, false); -test('Prints stdout, buffer: false, sync', testPrintOutputNoBuffer, 'full', false, true); -test('Prints stdout, buffer: false, fd-specific buffer, sync', testPrintOutputNoBuffer, 'full', {stdout: false}, true); -test('Prints stdout, buffer: false, fd-specific verbose, sync', testPrintOutputNoBuffer, stdoutFullOption, false, true); - -const testPrintOutputNoBufferFalse = async (t, verbose, buffer, isSync) => { - const {stderr} = await nestedSubprocess('noop.js', [foobarString], {verbose, buffer, isSync}); - t.is(getOutputLine(stderr), undefined); -}; - -test('Does not print stdout, buffer: false, fd-specific none', testPrintOutputNoBufferFalse, stdoutNoneOption, false, false); -test('Does not print stdout, buffer: false, different fd', testPrintOutputNoBufferFalse, stderrFullOption, false, false); -test('Does not print stdout, buffer: false, different fd, fd-specific buffer', testPrintOutputNoBufferFalse, stderrFullOption, {stdout: false}, false); -test('Does not print stdout, buffer: false, fd-specific none, sync', testPrintOutputNoBufferFalse, stdoutNoneOption, false, true); -test('Does not print stdout, buffer: false, different fd, sync', testPrintOutputNoBufferFalse, stderrFullOption, false, true); -test('Does not print stdout, buffer: false, different fd, fd-specific buffer, sync', testPrintOutputNoBufferFalse, stderrFullOption, {stdout: false}, true); - -const testPrintOutputNoBufferTransform = async (t, buffer, isSync) => { - const {stderr} = await nestedSubprocess('noop.js', [foobarString], { - optionsFixture: 'generator-uppercase.js', - verbose: 'full', - buffer, - isSync, - }); - t.is(getOutputLine(stderr), `${testTimestamp} [0] ${foobarUppercase}`); -}; - -test('Prints stdout, buffer: false, transform', testPrintOutputNoBufferTransform, false, false); -test('Prints stdout, buffer: false, transform, fd-specific buffer', testPrintOutputNoBufferTransform, {stdout: false}, false); -test('Prints stdout, buffer: false, transform, sync', testPrintOutputNoBufferTransform, false, true); -test('Prints stdout, buffer: false, transform, fd-specific buffer, sync', testPrintOutputNoBufferTransform, {stdout: false}, true); diff --git a/test/verbose/output-enable.js b/test/verbose/output-enable.js deleted file mode 100644 index 5820a1b08b..0000000000 --- a/test/verbose/output-enable.js +++ /dev/null @@ -1,145 +0,0 @@ -import test from 'ava'; -import {red} from 'yoctocolors'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString, foobarUtf16Uint8Array} from '../helpers/input.js'; -import {fullStdio} from '../helpers/stdio.js'; -import {nestedSubprocess} from '../helpers/nested.js'; -import { - runErrorSubprocess, - getOutputLine, - getOutputLines, - testTimestamp, - stdoutNoneOption, - stdoutShortOption, - stdoutFullOption, - stderrNoneOption, - stderrShortOption, - stderrFullOption, - fd3NoneOption, - fd3ShortOption, - fd3FullOption, -} from '../helpers/verbose.js'; - -setFixtureDirectory(); - -const testPrintOutput = async (t, verbose, fdNumber, isSync) => { - const {stderr} = await nestedSubprocess('noop-fd.js', [`${fdNumber}`, foobarString], {verbose, isSync}); - t.is(getOutputLine(stderr), `${testTimestamp} [0] ${foobarString}`); -}; - -test('Prints stdout, verbose "full"', testPrintOutput, 'full', 1, false); -test('Prints stderr, verbose "full"', testPrintOutput, 'full', 2, false); -test('Prints stdout, verbose "full", fd-specific', testPrintOutput, stdoutFullOption, 1, false); -test('Prints stderr, verbose "full", fd-specific', testPrintOutput, stderrFullOption, 2, false); -test('Prints stdout, verbose "full", sync', testPrintOutput, 'full', 1, true); -test('Prints stderr, verbose "full", sync', testPrintOutput, 'full', 2, true); -test('Prints stdout, verbose "full", fd-specific, sync', testPrintOutput, stdoutFullOption, 1, true); -test('Prints stderr, verbose "full", fd-specific, sync', testPrintOutput, stderrFullOption, 2, true); - -const testNoPrintOutput = async (t, verbose, fdNumber, isSync) => { - const {stderr} = await nestedSubprocess('noop-fd.js', [`${fdNumber}`, foobarString], {verbose, ...fullStdio, isSync}); - t.is(getOutputLine(stderr), undefined); -}; - -test('Does not print stdout, verbose default', testNoPrintOutput, undefined, 1, false); -test('Does not print stdout, verbose "none"', testNoPrintOutput, 'none', 1, false); -test('Does not print stdout, verbose "short"', testNoPrintOutput, 'short', 1, false); -test('Does not print stderr, verbose default', testNoPrintOutput, undefined, 2, false); -test('Does not print stderr, verbose "none"', testNoPrintOutput, 'none', 2, false); -test('Does not print stderr, verbose "short"', testNoPrintOutput, 'short', 2, false); -test('Does not print stdio[*], verbose default', testNoPrintOutput, undefined, 3, false); -test('Does not print stdio[*], verbose "none"', testNoPrintOutput, 'none', 3, false); -test('Does not print stdio[*], verbose "short"', testNoPrintOutput, 'short', 3, false); -test('Does not print stdio[*], verbose "full"', testNoPrintOutput, 'full', 3, false); -test('Does not print stdout, verbose default, fd-specific', testNoPrintOutput, {}, 1, false); -test('Does not print stdout, verbose "none", fd-specific', testNoPrintOutput, stdoutNoneOption, 1, false); -test('Does not print stdout, verbose "short", fd-specific', testNoPrintOutput, stdoutShortOption, 1, false); -test('Does not print stderr, verbose default, fd-specific', testNoPrintOutput, {}, 2, false); -test('Does not print stderr, verbose "none", fd-specific', testNoPrintOutput, stderrNoneOption, 2, false); -test('Does not print stderr, verbose "short", fd-specific', testNoPrintOutput, stderrShortOption, 2, false); -test('Does not print stdio[*], verbose default, fd-specific', testNoPrintOutput, {}, 3, false); -test('Does not print stdio[*], verbose "none", fd-specific', testNoPrintOutput, fd3NoneOption, 3, false); -test('Does not print stdio[*], verbose "short", fd-specific', testNoPrintOutput, fd3ShortOption, 3, false); -test('Does not print stdio[*], verbose "full", fd-specific', testNoPrintOutput, fd3FullOption, 3, false); -test('Does not print stdout, verbose default, sync', testNoPrintOutput, undefined, 1, true); -test('Does not print stdout, verbose "none", sync', testNoPrintOutput, 'none', 1, true); -test('Does not print stdout, verbose "short", sync', testNoPrintOutput, 'short', 1, true); -test('Does not print stderr, verbose default, sync', testNoPrintOutput, undefined, 2, true); -test('Does not print stderr, verbose "none", sync', testNoPrintOutput, 'none', 2, true); -test('Does not print stderr, verbose "short", sync', testNoPrintOutput, 'short', 2, true); -test('Does not print stdio[*], verbose default, sync', testNoPrintOutput, undefined, 3, true); -test('Does not print stdio[*], verbose "none", sync', testNoPrintOutput, 'none', 3, true); -test('Does not print stdio[*], verbose "short", sync', testNoPrintOutput, 'short', 3, true); -test('Does not print stdio[*], verbose "full", sync', testNoPrintOutput, 'full', 3, true); -test('Does not print stdout, verbose default, fd-specific, sync', testNoPrintOutput, {}, 1, true); -test('Does not print stdout, verbose "none", fd-specific, sync', testNoPrintOutput, stdoutNoneOption, 1, true); -test('Does not print stdout, verbose "short", fd-specific, sync', testNoPrintOutput, stdoutShortOption, 1, true); -test('Does not print stderr, verbose default, fd-specific, sync', testNoPrintOutput, {}, 2, true); -test('Does not print stderr, verbose "none", fd-specific, sync', testNoPrintOutput, stderrNoneOption, 2, true); -test('Does not print stderr, verbose "short", fd-specific, sync', testNoPrintOutput, stderrShortOption, 2, true); -test('Does not print stdio[*], verbose default, fd-specific, sync', testNoPrintOutput, {}, 3, true); -test('Does not print stdio[*], verbose "none", fd-specific, sync', testNoPrintOutput, fd3NoneOption, 3, true); -test('Does not print stdio[*], verbose "short", fd-specific, sync', testNoPrintOutput, fd3ShortOption, 3, true); -test('Does not print stdio[*], verbose "full", fd-specific, sync', testNoPrintOutput, fd3FullOption, 3, true); - -const testPrintError = async (t, isSync) => { - const stderr = await runErrorSubprocess(t, 'full', isSync); - t.is(getOutputLine(stderr), `${testTimestamp} [0] ${foobarString}`); -}; - -test('Prints stdout after errors', testPrintError, false); -test('Prints stdout after errors, sync', testPrintError, true); - -test('Does not quote spaces from stdout', async t => { - const {stderr} = await nestedSubprocess('noop.js', ['foo bar'], {verbose: 'full'}); - t.is(getOutputLine(stderr), `${testTimestamp} [0] foo bar`); -}); - -test('Does not quote special punctuation from stdout', async t => { - const {stderr} = await nestedSubprocess('noop.js', ['%'], {verbose: 'full'}); - t.is(getOutputLine(stderr), `${testTimestamp} [0] %`); -}); - -test('Does not escape internal characters from stdout', async t => { - const {stderr} = await nestedSubprocess('noop.js', ['ã'], {verbose: 'full'}); - t.is(getOutputLine(stderr), `${testTimestamp} [0] ã`); -}); - -test('Strips color sequences from stdout', async t => { - const {stderr} = await nestedSubprocess('noop.js', [red(foobarString)], {verbose: 'full'}, {env: {FORCE_COLOR: '1'}}); - t.is(getOutputLine(stderr), `${testTimestamp} [0] ${foobarString}`); -}); - -test('Escapes control characters from stdout', async t => { - const {stderr} = await nestedSubprocess('noop.js', ['\u0001'], {verbose: 'full'}); - t.is(getOutputLine(stderr), `${testTimestamp} [0] \\u0001`); -}); - -const testStdioSame = async (t, fdNumber) => { - const {nestedResult: {stdio}} = await nestedSubprocess('noop-fd.js', [`${fdNumber}`, foobarString], {verbose: 'full'}); - t.is(stdio[fdNumber], foobarString); -}; - -test('Does not change subprocess.stdout', testStdioSame, 1); -test('Does not change subprocess.stderr', testStdioSame, 2); - -const testSingleNewline = async (t, isSync) => { - const {stderr} = await nestedSubprocess('noop-fd.js', ['1', '\n'], {verbose: 'full', isSync}); - t.deepEqual(getOutputLines(stderr), [`${testTimestamp} [0] `]); -}; - -test('Prints stdout, single newline', testSingleNewline, false); -test('Prints stdout, single newline, sync', testSingleNewline, true); - -const testUtf16 = async (t, isSync) => { - const {stderr} = await nestedSubprocess('stdin.js', { - verbose: 'full', - input: foobarUtf16Uint8Array, - encoding: 'utf16le', - isSync, - }); - t.is(getOutputLine(stderr), `${testTimestamp} [0] ${foobarString}`); -}; - -test('Can use encoding UTF16, verbose "full"', testUtf16, false); -test('Can use encoding UTF16, verbose "full", sync', testUtf16, true); diff --git a/test/verbose/output-mixed.js b/test/verbose/output-mixed.js deleted file mode 100644 index 2f2e1deb63..0000000000 --- a/test/verbose/output-mixed.js +++ /dev/null @@ -1,85 +0,0 @@ -import {inspect} from 'node:util'; -import test from 'ava'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString, foobarObject} from '../helpers/input.js'; -import {simpleFull, noNewlinesChunks} from '../helpers/lines.js'; -import {nestedSubprocess} from '../helpers/nested.js'; -import {getOutputLine, getOutputLines, testTimestamp} from '../helpers/verbose.js'; - -setFixtureDirectory(); - -const testLines = async (t, lines, stripFinalNewline, isSync) => { - const {stderr} = await nestedSubprocess('noop-fd.js', ['1', simpleFull], { - verbose: 'full', - lines, - stripFinalNewline, - isSync, - }); - t.deepEqual(getOutputLines(stderr), noNewlinesChunks.map(line => `${testTimestamp} [0] ${line}`)); -}; - -test('Prints stdout, "lines: true"', testLines, true, false, false); -test('Prints stdout, "lines: true", fd-specific', testLines, {stdout: true}, false, false); -test('Prints stdout, "lines: true", stripFinalNewline', testLines, true, true, false); -test('Prints stdout, "lines: true", sync', testLines, true, false, true); -test('Prints stdout, "lines: true", fd-specific, sync', testLines, {stdout: true}, false, true); -test('Prints stdout, "lines: true", stripFinalNewline, sync', testLines, true, true, true); - -const testOnlyTransforms = async (t, isSync) => { - const {stderr} = await nestedSubprocess('noop.js', [foobarString], { - optionsFixture: 'generator-uppercase.js', - verbose: 'full', - isSync, - }); - t.is(getOutputLine(stderr), `${testTimestamp} [0] ${foobarString.toUpperCase()}`); -}; - -test('Prints stdout with only transforms', testOnlyTransforms, false); -test('Prints stdout with only transforms, sync', testOnlyTransforms, true); - -test('Prints stdout with only duplexes', async t => { - const {stderr} = await nestedSubprocess('noop.js', [foobarString], { - optionsFixture: 'generator-duplex.js', - verbose: 'full', - }); - t.is(getOutputLine(stderr), `${testTimestamp} [0] ${foobarString.toUpperCase()}`); -}); - -const testObjectMode = async (t, isSync) => { - const {stderr} = await nestedSubprocess('noop.js', { - optionsFixture: 'generator-object.js', - verbose: 'full', - isSync, - }); - t.is(getOutputLine(stderr), `${testTimestamp} [0] ${inspect(foobarObject)}`); -}; - -test('Prints stdout with object transforms', testObjectMode, false); -test('Prints stdout with object transforms, sync', testObjectMode, true); - -const testBigArray = async (t, isSync) => { - const {stderr} = await nestedSubprocess('noop.js', { - optionsFixture: 'generator-big-array.js', - verbose: 'full', - isSync, - }); - const lines = getOutputLines(stderr); - t.is(lines[0], `${testTimestamp} [0] [`); - t.true(lines[1].startsWith(`${testTimestamp} [0] 0, 1,`)); - t.is(lines.at(-1), `${testTimestamp} [0] ]`); -}; - -test('Prints stdout with big object transforms', testBigArray, false); -test('Prints stdout with big object transforms, sync', testBigArray, true); - -const testObjectModeString = async (t, isSync) => { - const {stderr} = await nestedSubprocess('noop.js', { - optionsFixture: 'generator-string-object.js', - verbose: 'full', - isSync, - }); - t.deepEqual(getOutputLines(stderr), noNewlinesChunks.map(line => `${testTimestamp} [0] ${line}`)); -}; - -test('Prints stdout with string transforms in objectMode', testObjectModeString, false); -test('Prints stdout with string transforms in objectMode, sync', testObjectModeString, true); diff --git a/test/verbose/output-noop.js b/test/verbose/output-noop.js deleted file mode 100644 index 44910a0984..0000000000 --- a/test/verbose/output-noop.js +++ /dev/null @@ -1,77 +0,0 @@ -import {rm, readFile} from 'node:fs/promises'; -import test from 'ava'; -import tempfile from 'tempfile'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString} from '../helpers/input.js'; -import {nestedSubprocess} from '../helpers/nested.js'; -import {getOutputLine, testTimestamp} from '../helpers/verbose.js'; - -setFixtureDirectory(); - -const testNoOutputOptions = async (t, isSync, options) => { - const {stderr} = await nestedSubprocess('noop.js', [foobarString], {verbose: 'full', isSync, ...options}); - t.is(getOutputLine(stderr), undefined); -}; - -test('Does not print stdout, encoding "buffer"', testNoOutputOptions, false, {encoding: 'buffer'}); -test('Does not print stdout, encoding "hex"', testNoOutputOptions, false, {encoding: 'hex'}); -test('Does not print stdout, encoding "base64"', testNoOutputOptions, false, {encoding: 'base64'}); -test('Does not print stdout, stdout "ignore"', testNoOutputOptions, false, {stdout: 'ignore'}); -test('Does not print stdout, stdout "inherit"', testNoOutputOptions, false, {stdout: 'inherit'}); -test('Does not print stdout, stdout 1', testNoOutputOptions, false, {stdout: 1}); -test('Does not print stdout, encoding "buffer", sync', testNoOutputOptions, true, {encoding: 'buffer'}); -test('Does not print stdout, encoding "hex", sync', testNoOutputOptions, true, {encoding: 'hex'}); -test('Does not print stdout, encoding "base64", sync', testNoOutputOptions, true, {encoding: 'base64'}); -test('Does not print stdout, stdout "ignore", sync', testNoOutputOptions, true, {stdout: 'ignore'}); -test('Does not print stdout, stdout "inherit", sync', testNoOutputOptions, true, {stdout: 'inherit'}); -test('Does not print stdout, stdout 1, sync', testNoOutputOptions, true, {stdout: 1}); - -const testNoOutputDynamic = async (t, isSync, optionsFixture) => { - const {stderr} = await nestedSubprocess('noop.js', [foobarString], {verbose: 'full', isSync, optionsFixture}); - t.is(getOutputLine(stderr), undefined); -}; - -test('Does not print stdout, stdout Writable', testNoOutputDynamic, false, 'writable.js'); -test('Does not print stdout, stdout WritableStream', testNoOutputDynamic, false, 'writable-web.js'); -test('Does not print stdout, stdout Writable, sync', testNoOutputDynamic, true, 'writable.js'); -test('Does not print stdout, stdout WritableStream, sync', testNoOutputDynamic, true, 'writable-web.js'); - -const testNoOutputStream = async (t, parentFixture) => { - const {stderr} = await nestedSubprocess('noop.js', [foobarString], {verbose: 'full', parentFixture}); - t.is(getOutputLine(stderr), undefined); -}; - -test('Does not print stdout, .pipe(stream)', testNoOutputStream, 'nested-pipe-stream.js'); -test('Does not print stdout, .pipe(subprocess)', testNoOutputStream, 'nested-pipe-subprocess.js'); - -const testStdoutFile = async (t, isSync, optionsFixture) => { - const file = tempfile(); - const {stderr} = await nestedSubprocess('noop.js', [foobarString], { - verbose: 'full', - stdout: {file}, - isSync, - optionsFixture, - }); - t.is(getOutputLine(stderr), undefined); - const contents = await readFile(file, 'utf8'); - t.is(contents.trim(), foobarString); - await rm(file); -}; - -test('Does not print stdout, stdout { file }', testStdoutFile, false); -test('Does not print stdout, stdout fileUrl', testStdoutFile, false, 'file-url.js'); -test('Does not print stdout, stdout { file }, sync', testStdoutFile, true); -test('Does not print stdout, stdout fileUrl, sync', testStdoutFile, true, 'file-url.js'); - -const testPrintOutputOptions = async (t, options, isSync) => { - const {stderr} = await nestedSubprocess('noop.js', [foobarString], {verbose: 'full', isSync, ...options}); - t.is(getOutputLine(stderr), `${testTimestamp} [0] ${foobarString}`); -}; - -test('Prints stdout, stdout "pipe"', testPrintOutputOptions, {stdout: 'pipe'}, false); -test('Prints stdout, stdout "overlapped"', testPrintOutputOptions, {stdout: 'overlapped'}, false); -test('Prints stdout, stdout null', testPrintOutputOptions, {stdout: null}, false); -test('Prints stdout, stdout ["pipe"]', testPrintOutputOptions, {stdout: ['pipe']}, false); -test('Prints stdout, stdout "pipe", sync', testPrintOutputOptions, {stdout: 'pipe'}, true); -test('Prints stdout, stdout null, sync', testPrintOutputOptions, {stdout: null}, true); -test('Prints stdout, stdout ["pipe"], sync', testPrintOutputOptions, {stdout: ['pipe']}, true); diff --git a/test/verbose/output-pipe.js b/test/verbose/output-pipe.js deleted file mode 100644 index 7cbb797ce0..0000000000 --- a/test/verbose/output-pipe.js +++ /dev/null @@ -1,48 +0,0 @@ -import test from 'ava'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString} from '../helpers/input.js'; -import {nestedSubprocess} from '../helpers/nested.js'; -import { - getOutputLine, - getOutputLines, - testTimestamp, - getVerboseOption, -} from '../helpers/verbose.js'; - -setFixtureDirectory(); - -const testPipeOutput = async (t, parentFixture, sourceVerbose, destinationVerbose) => { - const {stderr} = await nestedSubprocess('noop.js', [foobarString], { - parentFixture, - sourceOptions: getVerboseOption(sourceVerbose, 'full'), - destinationFile: 'stdin.js', - destinationOptions: getVerboseOption(destinationVerbose, 'full'), - }); - - const lines = getOutputLines(stderr); - const id = sourceVerbose && destinationVerbose ? 1 : 0; - t.deepEqual(lines, destinationVerbose - ? [`${testTimestamp} [${id}] ${foobarString}`] - : []); -}; - -test('Prints stdout if both verbose with .pipe("file")', testPipeOutput, 'nested-pipe-file.js', true, true); -test('Prints stdout if both verbose with .pipe`command`', testPipeOutput, 'nested-pipe-script.js', true, true); -test('Prints stdout if both verbose with .pipe(subprocess)', testPipeOutput, 'nested-pipe-subprocesses.js', true, true); -test('Prints stdout if only second verbose with .pipe("file")', testPipeOutput, 'nested-pipe-file.js', false, true); -test('Prints stdout if only second verbose with .pipe`command`', testPipeOutput, 'nested-pipe-script.js', false, true); -test('Prints stdout if only second verbose with .pipe(subprocess)', testPipeOutput, 'nested-pipe-subprocesses.js', false, true); -test('Does not print stdout if only first verbose with .pipe("file")', testPipeOutput, 'nested-pipe-file.js', true, false); -test('Does not print stdout if only first verbose with .pipe`command`', testPipeOutput, 'nested-pipe-script.js', true, false); -test('Does not print stdout if only first verbose with .pipe(subprocess)', testPipeOutput, 'nested-pipe-subprocesses.js', true, false); -test('Does not print stdout if neither verbose with .pipe("file")', testPipeOutput, 'nested-pipe-file.js', false, false); -test('Does not print stdout if neither verbose with .pipe`command`', testPipeOutput, 'nested-pipe-script.js', false, false); -test('Does not print stdout if neither verbose with .pipe(subprocess)', testPipeOutput, 'nested-pipe-subprocesses.js', false, false); - -const testPrintOutputFixture = async (t, parentFixture) => { - const {stderr} = await nestedSubprocess('noop.js', [foobarString], {parentFixture, verbose: 'full', unpipe: true}); - t.is(getOutputLine(stderr), `${testTimestamp} [0] ${foobarString}`); -}; - -test('Prints stdout, .pipe(stream) + .unpipe()', testPrintOutputFixture, 'nested-pipe-stream.js'); -test('Prints stdout, .pipe(subprocess) + .unpipe()', testPrintOutputFixture, 'nested-pipe-subprocess.js'); diff --git a/test/verbose/output-progressive.js b/test/verbose/output-progressive.js deleted file mode 100644 index ee7b4694ac..0000000000 --- a/test/verbose/output-progressive.js +++ /dev/null @@ -1,58 +0,0 @@ -import {on} from 'node:events'; -import test from 'ava'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString} from '../helpers/input.js'; -import {nestedSubprocess, nestedInstance} from '../helpers/nested.js'; -import {getOutputLine, getOutputLines, testTimestamp} from '../helpers/verbose.js'; - -setFixtureDirectory(); - -test('Prints stdout one line at a time', async t => { - const subprocess = nestedInstance('noop-progressive.js', [foobarString], {verbose: 'full'}); - - for await (const chunk of on(subprocess.stderr, 'data')) { - const outputLine = getOutputLine(chunk.toString().trim()); - if (outputLine !== undefined) { - t.is(outputLine, `${testTimestamp} [0] ${foobarString}`); - break; - } - } - - await subprocess; -}); - -test.serial('Prints stdout progressively, interleaved', async t => { - const subprocess = nestedInstance('noop-repeat.js', ['1', `${foobarString}\n`], {parentFixture: 'nested-double.js', verbose: 'full'}); - - let firstSubprocessPrinted = false; - let secondSubprocessPrinted = false; - for await (const chunk of on(subprocess.stderr, 'data')) { - const outputLine = getOutputLine(chunk.toString().trim()); - if (outputLine === undefined) { - continue; - } - - if (outputLine.includes(foobarString)) { - t.is(outputLine, `${testTimestamp} [0] ${foobarString}`); - firstSubprocessPrinted ||= true; - } else { - t.is(outputLine, `${testTimestamp} [1] ${foobarString.toUpperCase()}`); - secondSubprocessPrinted ||= true; - } - - if (firstSubprocessPrinted && secondSubprocessPrinted) { - break; - } - } - - subprocess.kill(); - await t.throwsAsync(subprocess); -}); - -const testInterleaved = async (t, expectedLines, isSync) => { - const {stderr} = await nestedSubprocess('noop-132.js', {verbose: 'full', isSync}); - t.deepEqual(getOutputLines(stderr), expectedLines.map(line => `${testTimestamp} [0] ${line}`)); -}; - -test('Prints stdout + stderr interleaved', testInterleaved, [1, 2, 3], false); -test('Prints stdout + stderr not interleaved, sync', testInterleaved, [1, 3, 2], true); diff --git a/test/verbose/start.js b/test/verbose/start.js deleted file mode 100644 index 78f38d08a4..0000000000 --- a/test/verbose/start.js +++ /dev/null @@ -1,162 +0,0 @@ -import test from 'ava'; -import {red} from 'yoctocolors'; -import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString} from '../helpers/input.js'; -import {nestedSubprocess} from '../helpers/nested.js'; -import { - QUOTE, - runErrorSubprocess, - runEarlyErrorSubprocess, - getCommandLine, - getCommandLines, - testTimestamp, - getVerboseOption, - stdoutNoneOption, - stdoutShortOption, - stdoutFullOption, - stderrNoneOption, - stderrShortOption, - stderrFullOption, - fd3NoneOption, - fd3ShortOption, - fd3FullOption, - ipcNoneOption, - ipcShortOption, - ipcFullOption, -} from '../helpers/verbose.js'; - -setFixtureDirectory(); - -const testPrintCommand = async (t, verbose, worker, isSync) => { - const {stderr} = await nestedSubprocess('noop.js', [foobarString], {verbose, worker, isSync}); - t.is(getCommandLine(stderr), `${testTimestamp} [0] $ noop.js ${foobarString}`); -}; - -test('Prints command, verbose "short"', testPrintCommand, 'short', false, false); -test('Prints command, verbose "full"', testPrintCommand, 'full', false, false); -test('Prints command, verbose "short", fd-specific stdout', testPrintCommand, stdoutShortOption, false, false); -test('Prints command, verbose "full", fd-specific stdout', testPrintCommand, stdoutFullOption, false, false); -test('Prints command, verbose "short", fd-specific stderr', testPrintCommand, stderrShortOption, false, false); -test('Prints command, verbose "full", fd-specific stderr', testPrintCommand, stderrFullOption, false, false); -test('Prints command, verbose "short", fd-specific fd3', testPrintCommand, fd3ShortOption, false, false); -test('Prints command, verbose "full", fd-specific fd3', testPrintCommand, fd3FullOption, false, false); -test('Prints command, verbose "short", fd-specific ipc', testPrintCommand, ipcShortOption, false, false); -test('Prints command, verbose "full", fd-specific ipc', testPrintCommand, ipcFullOption, false, false); -test('Prints command, verbose "short", sync', testPrintCommand, 'short', false, true); -test('Prints command, verbose "full", sync', testPrintCommand, 'full', false, true); -test('Prints command, verbose "short", fd-specific stdout, sync', testPrintCommand, stdoutShortOption, false, true); -test('Prints command, verbose "full", fd-specific stdout, sync', testPrintCommand, stdoutFullOption, false, true); -test('Prints command, verbose "short", fd-specific stderr, sync', testPrintCommand, stderrShortOption, false, true); -test('Prints command, verbose "full", fd-specific stderr, sync', testPrintCommand, stderrFullOption, false, true); -test('Prints command, verbose "short", fd-specific fd3, sync', testPrintCommand, fd3ShortOption, false, true); -test('Prints command, verbose "full", fd-specific fd3, sync', testPrintCommand, fd3FullOption, false, true); -test('Prints command, verbose "short", fd-specific ipc, sync', testPrintCommand, ipcShortOption, false, true); -test('Prints command, verbose "full", fd-specific ipc, sync', testPrintCommand, ipcFullOption, false, true); -test('Prints command, verbose "short", worker', testPrintCommand, 'short', true, false); -test('Prints command, verbose "full", worker', testPrintCommand, 'full', true, false); -test('Prints command, verbose "short", fd-specific stdout, worker', testPrintCommand, stdoutShortOption, true, false); -test('Prints command, verbose "full", fd-specific stdout, worker', testPrintCommand, stdoutFullOption, true, false); -test('Prints command, verbose "short", fd-specific stderr, worker', testPrintCommand, stderrShortOption, true, false); -test('Prints command, verbose "full", fd-specific stderr, worker', testPrintCommand, stderrFullOption, true, false); -test('Prints command, verbose "short", fd-specific fd3, worker', testPrintCommand, fd3ShortOption, true, false); -test('Prints command, verbose "full", fd-specific fd3, worker', testPrintCommand, fd3FullOption, true, false); -test('Prints command, verbose "short", fd-specific ipc, worker', testPrintCommand, ipcShortOption, true, false); -test('Prints command, verbose "full", fd-specific ipc, worker', testPrintCommand, ipcFullOption, true, false); -test('Prints command, verbose "short", worker, sync', testPrintCommand, 'short', true, true); -test('Prints command, verbose "full", worker, sync', testPrintCommand, 'full', true, true); -test('Prints command, verbose "short", fd-specific stdout, worker, sync', testPrintCommand, stdoutShortOption, true, true); -test('Prints command, verbose "full", fd-specific stdout, worker, sync', testPrintCommand, stdoutFullOption, true, true); -test('Prints command, verbose "short", fd-specific stderr, worker, sync', testPrintCommand, stderrShortOption, true, true); -test('Prints command, verbose "full", fd-specific stderr, worker, sync', testPrintCommand, stderrFullOption, true, true); -test('Prints command, verbose "short", fd-specific fd3, worker, sync', testPrintCommand, fd3ShortOption, true, true); -test('Prints command, verbose "full", fd-specific fd3, worker, sync', testPrintCommand, fd3FullOption, true, true); -test('Prints command, verbose "short", fd-specific ipc, worker, sync', testPrintCommand, ipcShortOption, true, true); -test('Prints command, verbose "full", fd-specific ipc, worker, sync', testPrintCommand, ipcFullOption, true, true); - -const testNoPrintCommand = async (t, verbose, isSync) => { - const {stderr} = await nestedSubprocess('noop.js', [foobarString], {verbose, isSync}); - t.is(stderr, ''); -}; - -test('Does not print command, verbose "none"', testNoPrintCommand, 'none', false); -test('Does not print command, verbose default', testNoPrintCommand, undefined, false); -test('Does not print command, verbose "none", fd-specific stdout', testNoPrintCommand, stdoutNoneOption, false); -test('Does not print command, verbose "none", fd-specific stderr', testNoPrintCommand, stderrNoneOption, false); -test('Does not print command, verbose "none", fd-specific fd3', testNoPrintCommand, fd3NoneOption, false); -test('Does not print command, verbose "none", fd-specific ipc', testNoPrintCommand, ipcNoneOption, false); -test('Does not print command, verbose default, fd-specific', testNoPrintCommand, {}, false); -test('Does not print command, verbose "none", sync', testNoPrintCommand, 'none', true); -test('Does not print command, verbose default, sync', testNoPrintCommand, undefined, true); -test('Does not print command, verbose "none", fd-specific stdout, sync', testNoPrintCommand, stdoutNoneOption, true); -test('Does not print command, verbose "none", fd-specific stderr, sync', testNoPrintCommand, stderrNoneOption, true); -test('Does not print command, verbose "none", fd-specific fd3, sync', testNoPrintCommand, fd3NoneOption, true); -test('Does not print command, verbose "none", fd-specific ipc, sync', testNoPrintCommand, ipcNoneOption, true); -test('Does not print command, verbose default, fd-specific, sync', testNoPrintCommand, {}, true); - -const testPrintCommandError = async (t, isSync) => { - const stderr = await runErrorSubprocess(t, 'short', isSync); - t.is(getCommandLine(stderr), `${testTimestamp} [0] $ noop-fail.js 1 ${foobarString}`); -}; - -test('Prints command after errors', testPrintCommandError, false); -test('Prints command after errors, sync', testPrintCommandError, true); - -const testPrintCommandEarly = async (t, isSync) => { - const stderr = await runEarlyErrorSubprocess(t, isSync); - t.is(getCommandLine(stderr), `${testTimestamp} [0] $ noop.js ${foobarString}`); -}; - -test('Prints command before early validation errors', testPrintCommandEarly, false); -test('Prints command before early validation errors, sync', testPrintCommandEarly, true); - -const testPipeCommand = async (t, parentFixture, sourceVerbose, destinationVerbose) => { - const {stderr} = await nestedSubprocess('noop.js', [foobarString], { - parentFixture, - sourceOptions: getVerboseOption(sourceVerbose), - destinationFile: 'stdin.js', - destinationOptions: getVerboseOption(destinationVerbose), - }); - - const pipeSymbol = parentFixture === 'nested-pipe-subprocesses.js' ? '$' : '|'; - const lines = getCommandLines(stderr); - t.is(lines.includes(`${testTimestamp} [0] $ noop.js ${foobarString}`), sourceVerbose); - t.is(lines.includes(`${testTimestamp} [${sourceVerbose ? 1 : 0}] ${pipeSymbol} stdin.js`), destinationVerbose); -}; - -test('Prints both commands piped with .pipe("file")', testPipeCommand, 'nested-pipe-file.js', true, true); -test('Prints both commands piped with .pipe`command`', testPipeCommand, 'nested-pipe-script.js', true, true); -test('Prints both commands piped with .pipe(subprocess)', testPipeCommand, 'nested-pipe-subprocesses.js', true, true); -test('Prints first command piped with .pipe("file")', testPipeCommand, 'nested-pipe-file.js', true, false); -test('Prints first command piped with .pipe`command`', testPipeCommand, 'nested-pipe-script.js', true, false); -test('Prints first command piped with .pipe(subprocess)', testPipeCommand, 'nested-pipe-subprocesses.js', true, false); -test('Prints second command piped with .pipe("file")', testPipeCommand, 'nested-pipe-file.js', false, true); -test('Prints second command piped with .pipe`command`', testPipeCommand, 'nested-pipe-script.js', false, true); -test('Prints second command piped with .pipe(subprocess)', testPipeCommand, 'nested-pipe-subprocesses.js', false, true); -test('Prints neither commands piped with .pipe("file")', testPipeCommand, 'nested-pipe-file.js', false, false); -test('Prints neither commands piped with .pipe`command`', testPipeCommand, 'nested-pipe-script.js', false, false); -test('Prints neither commands piped with .pipe(subprocess)', testPipeCommand, 'nested-pipe-subprocesses.js', false, false); - -test('Quotes spaces from command', async t => { - const {stderr} = await nestedSubprocess('noop.js', ['foo bar'], {verbose: 'short'}); - t.is(getCommandLine(stderr), `${testTimestamp} [0] $ noop.js ${QUOTE}foo bar${QUOTE}`); -}); - -test('Quotes special punctuation from command', async t => { - const {stderr} = await nestedSubprocess('noop.js', ['%'], {verbose: 'short'}); - t.is(getCommandLine(stderr), `${testTimestamp} [0] $ noop.js ${QUOTE}%${QUOTE}`); -}); - -test('Does not escape internal characters from command', async t => { - const {stderr} = await nestedSubprocess('noop.js', ['ã'], {verbose: 'short'}); - t.is(getCommandLine(stderr), `${testTimestamp} [0] $ noop.js ${QUOTE}ã${QUOTE}`); -}); - -test('Escapes color sequences from command', async t => { - const {stderr} = await nestedSubprocess('noop.js', [red(foobarString)], {verbose: 'short'}, {env: {FORCE_COLOR: '1'}}); - t.true(getCommandLine(stderr).includes(`${QUOTE}\\u001b[31m${foobarString}\\u001b[39m${QUOTE}`)); -}); - -test('Escapes control characters from command', async t => { - const {stderr} = await nestedSubprocess('noop.js', ['\u0001'], {verbose: 'short'}); - t.is(getCommandLine(stderr), `${testTimestamp} [0] $ noop.js ${QUOTE}\\u0001${QUOTE}`); -});