1// Flags: --expose-internals 2'use strict'; 3const common = require('../common'); 4const assert = require('assert'); 5const cp = require('child_process'); 6const internalCp = require('internal/child_process'); 7const oldSpawnSync = internalCp.spawnSync; 8 9// Verify that a shell is, in fact, executed 10const doesNotExist = cp.spawnSync('does-not-exist', { shell: true }); 11 12assert.notStrictEqual(doesNotExist.file, 'does-not-exist'); 13assert.strictEqual(doesNotExist.error, undefined); 14assert.strictEqual(doesNotExist.signal, null); 15 16if (common.isWindows) 17 assert.strictEqual(doesNotExist.status, 1); // Exit code of cmd.exe 18else 19 assert.strictEqual(doesNotExist.status, 127); // Exit code of /bin/sh 20 21// Verify that passing arguments works 22internalCp.spawnSync = common.mustCall(function(opts) { 23 assert.strictEqual(opts.args[opts.args.length - 1].replace(/"/g, ''), 24 'echo foo'); 25 return oldSpawnSync(opts); 26}); 27const echo = cp.spawnSync('echo', ['foo'], { shell: true }); 28internalCp.spawnSync = oldSpawnSync; 29 30assert.strictEqual(echo.stdout.toString().trim(), 'foo'); 31 32// Verify that shell features can be used 33const cmd = 'echo bar | cat'; 34const command = cp.spawnSync(cmd, { shell: true }); 35 36assert.strictEqual(command.stdout.toString().trim(), 'bar'); 37 38// Verify that the environment is properly inherited 39const env = cp.spawnSync(`"${process.execPath}" -pe process.env.BAZ`, { 40 env: { ...process.env, BAZ: 'buzz' }, 41 shell: true 42}); 43 44assert.strictEqual(env.stdout.toString().trim(), 'buzz'); 45 46// Verify that the shell internals work properly across platforms. 47{ 48 const originalComspec = process.env.comspec; 49 50 // Enable monkey patching process.platform. 51 const originalPlatform = process.platform; 52 let platform = null; 53 Object.defineProperty(process, 'platform', { get: () => platform }); 54 55 function test(testPlatform, shell, shellOutput) { 56 platform = testPlatform; 57 const isCmd = /^(?:.*\\)?cmd(?:\.exe)?$/i.test(shellOutput); 58 const cmd = 'not_a_real_command'; 59 60 const shellFlags = isCmd ? ['/d', '/s', '/c'] : ['-c']; 61 const outputCmd = isCmd ? `"${cmd}"` : cmd; 62 const windowsVerbatim = isCmd ? true : undefined; 63 internalCp.spawnSync = common.mustCall(function(opts) { 64 assert.strictEqual(opts.file, shellOutput); 65 assert.deepStrictEqual(opts.args, 66 [shellOutput, ...shellFlags, outputCmd]); 67 assert.strictEqual(opts.shell, shell); 68 assert.strictEqual(opts.file, opts.file); 69 assert.deepStrictEqual(opts.args, opts.args); 70 assert.strictEqual(opts.windowsHide, false); 71 assert.strictEqual(opts.windowsVerbatimArguments, 72 !!windowsVerbatim); 73 }); 74 cp.spawnSync(cmd, { shell }); 75 internalCp.spawnSync = oldSpawnSync; 76 } 77 78 // Test Unix platforms with the default shell. 79 test('darwin', true, '/bin/sh'); 80 81 // Test Unix platforms with a user specified shell. 82 test('darwin', '/bin/csh', '/bin/csh'); 83 84 // Test Android platforms. 85 test('android', true, '/system/bin/sh'); 86 87 // Test Windows platforms with a user specified shell. 88 test('win32', 'powershell.exe', 'powershell.exe'); 89 90 // Test Windows platforms with the default shell and no comspec. 91 delete process.env.comspec; 92 test('win32', true, 'cmd.exe'); 93 94 // Test Windows platforms with the default shell and a comspec value. 95 process.env.comspec = 'powershell.exe'; 96 test('win32', true, process.env.comspec); 97 98 // Restore the original value of process.platform. 99 platform = originalPlatform; 100 101 // Restore the original comspec environment variable if necessary. 102 if (originalComspec) 103 process.env.comspec = originalComspec; 104} 105