1'use strict'; 2 3const common = require('../common'); 4const assert = require('assert'); 5const { execFile, execFileSync } = require('child_process'); 6const { getEventListeners } = require('events'); 7const { getSystemErrorName } = require('util'); 8const fixtures = require('../common/fixtures'); 9const os = require('os'); 10 11const fixture = fixtures.path('exit.js'); 12const echoFixture = fixtures.path('echo.js'); 13const execOpts = { encoding: 'utf8', shell: true }; 14 15{ 16 execFile( 17 process.execPath, 18 [fixture, 42], 19 common.mustCall((e) => { 20 // Check that arguments are included in message 21 assert.strictEqual(e.message.trim(), 22 `Command failed: ${process.execPath} ${fixture} 42`); 23 assert.strictEqual(e.code, 42); 24 }) 25 ); 26} 27 28{ 29 // Verify that negative exit codes can be translated to UV error names. 30 const errorString = `Error: Command failed: ${process.execPath}`; 31 const code = -1; 32 const callback = common.mustCall((err, stdout, stderr) => { 33 assert.strictEqual(err.toString().trim(), errorString); 34 assert.strictEqual(err.code, getSystemErrorName(code)); 35 assert.strictEqual(err.killed, true); 36 assert.strictEqual(err.signal, null); 37 assert.strictEqual(err.cmd, process.execPath); 38 assert.strictEqual(stdout.trim(), ''); 39 assert.strictEqual(stderr.trim(), ''); 40 }); 41 const child = execFile(process.execPath, callback); 42 43 child.kill(); 44 child.emit('close', code, null); 45} 46 47{ 48 // Verify the shell option works properly 49 execFile(process.execPath, [fixture, 0], execOpts, common.mustSucceed()); 50} 51 52{ 53 // Verify that the signal option works properly 54 const ac = new AbortController(); 55 const { signal } = ac; 56 57 const test = () => { 58 const check = common.mustCall((err) => { 59 assert.strictEqual(err.code, 'ABORT_ERR'); 60 assert.strictEqual(err.name, 'AbortError'); 61 assert.strictEqual(err.signal, undefined); 62 }); 63 execFile(process.execPath, [echoFixture, 0], { signal }, check); 64 }; 65 66 // Verify that it still works the same way now that the signal is aborted. 67 test(); 68 ac.abort(); 69} 70 71{ 72 // Verify that does not spawn a child if already aborted 73 const signal = AbortSignal.abort(); 74 75 const check = common.mustCall((err) => { 76 assert.strictEqual(err.code, 'ABORT_ERR'); 77 assert.strictEqual(err.name, 'AbortError'); 78 assert.strictEqual(err.signal, undefined); 79 }); 80 execFile(process.execPath, [echoFixture, 0], { signal }, check); 81} 82 83{ 84 // Verify that if something different than Abortcontroller.signal 85 // is passed, ERR_INVALID_ARG_TYPE is thrown 86 assert.throws(() => { 87 const callback = common.mustNotCall(); 88 89 execFile(process.execPath, [echoFixture, 0], { signal: 'hello' }, callback); 90 }, { code: 'ERR_INVALID_ARG_TYPE', name: 'TypeError' }); 91} 92{ 93 // Verify that the process completing removes the abort listener 94 const ac = new AbortController(); 95 const { signal } = ac; 96 97 const callback = common.mustCall((err) => { 98 assert.strictEqual(getEventListeners(ac.signal).length, 0); 99 assert.strictEqual(err, null); 100 }); 101 execFile(process.execPath, [fixture, 0], { signal }, callback); 102} 103 104// Verify the execFile() stdout is the same as execFileSync(). 105{ 106 const file = 'echo'; 107 const args = ['foo', 'bar']; 108 109 // Test with and without `{ shell: true }` 110 [ 111 // Skipping shell-less test on Windows because its echo command is a shell built-in command. 112 ...(common.isWindows ? [] : [{ encoding: 'utf8' }]), 113 { shell: true, encoding: 'utf8' }, 114 ].forEach((options) => { 115 const execFileSyncStdout = execFileSync(file, args, options); 116 assert.strictEqual(execFileSyncStdout, `foo bar${os.EOL}`); 117 118 execFile(file, args, options, common.mustCall((_, stdout) => { 119 assert.strictEqual(stdout, execFileSyncStdout); 120 })); 121 }); 122} 123