From 799443c9942fa145320d9cc7c4638bdaa8c8d67a Mon Sep 17 00:00:00 2001 From: Rui Abreu Ferreira Date: Sat, 25 Mar 2017 22:47:38 +0000 Subject: win/test: Enable more system() tests --- test/functional/eval/system_spec.lua | 74 ++++++++++++++++++++++++++---------- 1 file changed, 53 insertions(+), 21 deletions(-) (limited to 'test/functional/eval') diff --git a/test/functional/eval/system_spec.lua b/test/functional/eval/system_spec.lua index bf95752e3b..83d8028b56 100644 --- a/test/functional/eval/system_spec.lua +++ b/test/functional/eval/system_spec.lua @@ -94,17 +94,26 @@ describe('system()', function() end) end) - if helpers.pending_win32(pending) then return end - it('sets v:shell_error', function() - eval([[system("sh -c 'exit'")]]) - eq(0, eval('v:shell_error')) - eval([[system("sh -c 'exit 1'")]]) - eq(1, eval('v:shell_error')) - eval([[system("sh -c 'exit 5'")]]) - eq(5, eval('v:shell_error')) - eval([[system('this-should-not-exist')]]) - eq(127, eval('v:shell_error')) + if helpers.os_name() == 'windows' then + eval([[system("cmd.exe /c exit")]]) + eq(0, eval('v:shell_error')) + eval([[system("cmd.exe /c exit 1")]]) + eq(1, eval('v:shell_error')) + eval([[system("cmd.exe /c exit 5")]]) + eq(5, eval('v:shell_error')) + eval([[system('this-should-not-exist')]]) + eq(1, eval('v:shell_error')) + else + eval([[system("sh -c 'exit'")]]) + eq(0, eval('v:shell_error')) + eval([[system("sh -c 'exit 1'")]]) + eq(1, eval('v:shell_error')) + eval([[system("sh -c 'exit 5'")]]) + eq(5, eval('v:shell_error')) + eval([[system('this-should-not-exist')]]) + eq(127, eval('v:shell_error')) + end end) describe('executes shell function if passed a string', function() @@ -120,6 +129,15 @@ describe('system()', function() screen:detach() end) + it('escapes inner double quotes #6329', function() + if helpers.os_name() == 'windows' then + -- In Windows cmd.exe's echo prints the quotes + eq('""\n', eval([[system('echo ""')]])) + else + eq('\n', eval([[system('echo ""')]])) + end + end) + it('`echo` and waits for its return', function() feed(':call system("echo")') screen:expect([[ @@ -180,7 +198,11 @@ describe('system()', function() describe('passing no input', function() it('returns the program output', function() - eq("echoed", eval('system("echo -n echoed")')) + if helpers.os_name() == 'windows' then + eq("echoed\n", eval('system("echo echoed")')) + else + eq("echoed", eval('system("echo -n echoed")')) + end end) it('to backgrounded command does not crash', function() -- This is indeterminate, just exercise the codepath. May get E5677. @@ -277,21 +299,30 @@ describe('system()', function() end) end) -if helpers.pending_win32(pending) then return end - describe('systemlist()', function() -- Similar to `system()`, but returns List instead of String. before_each(clear) it('sets the v:shell_error variable', function() - eval([[systemlist("sh -c 'exit'")]]) - eq(0, eval('v:shell_error')) - eval([[systemlist("sh -c 'exit 1'")]]) - eq(1, eval('v:shell_error')) - eval([[systemlist("sh -c 'exit 5'")]]) - eq(5, eval('v:shell_error')) - eval([[systemlist('this-should-not-exist')]]) - eq(127, eval('v:shell_error')) + if helpers.os_name() == 'windows' then + eval([[systemlist("cmd.exe /c exit")]]) + eq(0, eval('v:shell_error')) + eval([[systemlist("cmd.exe /c exit 1")]]) + eq(1, eval('v:shell_error')) + eval([[systemlist("cmd.exe /c exit 5")]]) + eq(5, eval('v:shell_error')) + eval([[systemlist('this-should-not-exist')]]) + eq(1, eval('v:shell_error')) + else + eval([[systemlist("sh -c 'exit'")]]) + eq(0, eval('v:shell_error')) + eval([[systemlist("sh -c 'exit 1'")]]) + eq(1, eval('v:shell_error')) + eval([[systemlist("sh -c 'exit 5'")]]) + eq(5, eval('v:shell_error')) + eval([[systemlist('this-should-not-exist')]]) + eq(127, eval('v:shell_error')) + end end) describe('exectues shell function', function() @@ -389,6 +420,7 @@ describe('systemlist()', function() after_each(delete_file(fname)) it('replaces NULs by newline characters', function() + if helpers.pending_win32(pending) then return end eq({'part1\npart2\npart3'}, eval('systemlist("cat '..fname..'")')) end) end) -- cgit From f3cc843755a6d638ada77dc31721aa53b3ff2364 Mon Sep 17 00:00:00 2001 From: Rui Abreu Ferreira Date: Tue, 28 Mar 2017 16:03:53 +0100 Subject: win: libuv_process_spawn(): special-case cmd.exe Disable CommandLineToArgvW-standard quoting for cmd.exe. libuv assumes spawned processes follow the convention expected by CommandLineToArgvW(). But cmd.exe is non-conformant, so for cmd.exe: - With system([]), the caller has full control (and responsibility) to quote arguments correctly. - With system(''), shell* options are used. libuv quoting is disabled if argv[0] is: - cmd.exe - cmd - $COMSPEC resolving to a path with filename cmd.exe Closes #6329 References #6387 --- test/functional/eval/system_spec.lua | 38 +++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) (limited to 'test/functional/eval') diff --git a/test/functional/eval/system_spec.lua b/test/functional/eval/system_spec.lua index 83d8028b56..45ca69707d 100644 --- a/test/functional/eval/system_spec.lua +++ b/test/functional/eval/system_spec.lua @@ -129,14 +129,38 @@ describe('system()', function() screen:detach() end) - it('escapes inner double quotes #6329', function() - if helpers.os_name() == 'windows' then - -- In Windows cmd.exe's echo prints the quotes + if helpers.os_name() == 'windows' then + it('with the default cmd.exe shell', function() eq('""\n', eval([[system('echo ""')]])) - else - eq('\n', eval([[system('echo ""')]])) - end - end) + eq('"a b"\n', eval([[system('echo "a b"')]])) + -- TODO: consistent with Vim, but it should be fixed + eq('a & echo b\n', eval([[system('echo a & echo b')]])) + eval([[system('cd "C:\Program Files"')]]) + eq(0, eval('v:shell_error')) + end) + + it('with set shell="cmd"', function() + helpers.source('let &shell="cmd"') + eq('"a b"\n', eval([[system('echo "a b"')]])) + end) + + it('works with cmd.exe from $COMSPEC', function() + local comspecshell = eval("fnamemodify($COMSPEC, ':t')") + if comspecshell == 'cmd.exe' then + helpers.source('let &shell=$COMSPEC') + eq('"a b"\n', eval([[system('echo "a b"')]])) + else + pending('$COMSPEC is not cmd.exe ' .. comspecshell) + end + end) + + it('works with powershell', function() + helpers.set_shell_powershell() + eq('a\nb\n', eval([[system('echo a b')]])) + eq('C:\\\n', eval([[system('cd c:\; (Get-Location).Path')]])) + eq('a b\n', eval([[system('echo "a b"')]])) + end) + end it('`echo` and waits for its return', function() feed(':call system("echo")') -- cgit From d31d177a0c2c9997c2cdb04975bc3354b9a23fb8 Mon Sep 17 00:00:00 2001 From: Rui Abreu Ferreira Date: Thu, 30 Mar 2017 23:41:52 +0100 Subject: win: default shellxescape, shellxquote to empty Calling cmd.exe in Windows follows a very different pattern from Vim. The primary difference is that Vim does a nested call to cmd.exe, e.g. the following call in Vim system('echo a 2>&1') spawns the following processes "C:\Program Files (x86)\Vim\vim80\vimrun" -s C:\Windows\system32\cmd.exe /c (echo a 2^>^&1 ^>C:\Users\dummy\AppData\Local\Temp\VIoC169.tmp 2^>^&1) C:\Windows\system32\cmd.exe /c C:\Windows\system32\cmd.exe /c (echo a 2^>^&1 ^>C:\Users\dummy\AppData\Local\Temp\VIo3C6C.tmp 2^>^&1) C:\Windows\system32\cmd.exe /c (echo a 2>&1 >C:\Users\dummy\AppData\Local\Temp\VIo3C6C.tmp 2>&1) The escaping with ^ is needed because cmd.exe calls itself and needs to preserve the special metacharacters for the last call. However in nvim no nested call is made, system('') spawns a single cmd.exe process. Setting shellxescape to "" disables escaping with ^. The previous default for shellxquote=( wrapped any command in parenthesis, in Vim this is more meaningful due to the use of tempfiles to store the output and redirection (also see &shellquote). There is a slight benefit in having the default be empty because some expressions that run in console will not run within parens e.g. due to unbalanced double quotes system('echo "a b') --- test/functional/eval/system_spec.lua | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) (limited to 'test/functional/eval') diff --git a/test/functional/eval/system_spec.lua b/test/functional/eval/system_spec.lua index 45ca69707d..7e213e2156 100644 --- a/test/functional/eval/system_spec.lua +++ b/test/functional/eval/system_spec.lua @@ -4,6 +4,8 @@ local nvim_dir = helpers.nvim_dir local eq, call, clear, eval, feed_command, feed, nvim = helpers.eq, helpers.call, helpers.clear, helpers.eval, helpers.feed_command, helpers.feed, helpers.nvim +local command = helpers.command +local iswin = helpers.iswin local Screen = require('test.functional.ui.screen') @@ -33,8 +35,7 @@ describe('system()', function() describe('command passed as a List', function() local function printargs_path() - return nvim_dir..'/printargs-test' - .. (helpers.os_name() == 'windows' and '.exe' or '') + return nvim_dir..'/printargs-test' .. (iswin() and '.exe' or '') end it('sets v:shell_error if cmd[0] is not executable', function() @@ -88,14 +89,14 @@ describe('system()', function() end) it('does NOT run in shell', function() - if helpers.os_name() ~= 'windows' then + if not iswin() then eq("* $PATH %PATH%\n", eval("system(['echo', '*', '$PATH', '%PATH%'])")) end end) end) it('sets v:shell_error', function() - if helpers.os_name() == 'windows' then + if iswin() then eval([[system("cmd.exe /c exit")]]) eq(0, eval('v:shell_error')) eval([[system("cmd.exe /c exit 1")]]) @@ -129,28 +130,29 @@ describe('system()', function() screen:detach() end) - if helpers.os_name() == 'windows' then - it('with the default cmd.exe shell', function() + if iswin() then + it('with shell=cmd.exe', function() + command('set shell=cmd.exe') eq('""\n', eval([[system('echo ""')]])) eq('"a b"\n', eval([[system('echo "a b"')]])) - -- TODO: consistent with Vim, but it should be fixed - eq('a & echo b\n', eval([[system('echo a & echo b')]])) + eq('a \nb\n', eval([[system('echo a & echo b')]])) + eq('a \n', eval([[system('echo a 2>&1')]])) eval([[system('cd "C:\Program Files"')]]) eq(0, eval('v:shell_error')) end) - it('with set shell="cmd"', function() - helpers.source('let &shell="cmd"') + it('with shell=cmd', function() + command('set shell=cmd') eq('"a b"\n', eval([[system('echo "a b"')]])) end) - it('works with cmd.exe from $COMSPEC', function() + it('with shell=$COMSPEC', function() local comspecshell = eval("fnamemodify($COMSPEC, ':t')") if comspecshell == 'cmd.exe' then - helpers.source('let &shell=$COMSPEC') + command('set shell=$COMSPEC') eq('"a b"\n', eval([[system('echo "a b"')]])) else - pending('$COMSPEC is not cmd.exe ' .. comspecshell) + pending('$COMSPEC is not cmd.exe: ' .. comspecshell) end end) @@ -222,7 +224,7 @@ describe('system()', function() describe('passing no input', function() it('returns the program output', function() - if helpers.os_name() == 'windows' then + if iswin() then eq("echoed\n", eval('system("echo echoed")')) else eq("echoed", eval('system("echo -n echoed")')) @@ -327,8 +329,8 @@ describe('systemlist()', function() -- Similar to `system()`, but returns List instead of String. before_each(clear) - it('sets the v:shell_error variable', function() - if helpers.os_name() == 'windows' then + it('sets v:shell_error', function() + if iswin() then eval([[systemlist("cmd.exe /c exit")]]) eq(0, eval('v:shell_error')) eval([[systemlist("cmd.exe /c exit 1")]]) -- cgit