From 175892fa373a6a06c19af846f93a95240dc02b66 Mon Sep 17 00:00:00 2001 From: Enan Ajmain <3nan.ajmain@gmail.com> Date: Tue, 9 Nov 2021 20:22:21 +0600 Subject: [PATCH 1/2] fix: make_filter_cmd for :! powershell #15913 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: Nvim fails to create tempfile "…/nvim6UJx04/7" when 'shell' is set to pwsh (PowerShell Core). This breaks filtered shell commands ":{range}!". With shell set to cmd, it works. Solution: PowerShell doesn't use "<" for stdin redirection. Instead, use "-RedirectStandardInput". Closes #15913 --- runtime/doc/options.txt | 4 +-- src/nvim/ex_cmds.c | 32 ++++++++++++++++++----- test/functional/helpers.lua | 4 +-- test/functional/vimscript/system_spec.lua | 28 ++++++++++++++++++++ 4 files changed, 58 insertions(+), 10 deletions(-) diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index e840b504e3995e..14747d967d50c7 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -5324,9 +5324,9 @@ A jump table for the options with a short description can be found at |Q_op|. unescaping, so to keep yourself sane use |:let-&| like shown above. *shell-powershell* To use PowerShell: > - let &shell = has('win32') ? 'powershell' : 'pwsh' + let &shell = executable('pwsh') ? 'pwsh' : 'powershell' let &shellcmdflag = '-NoLogo -NoProfile -ExecutionPolicy RemoteSigned -Command [Console]::InputEncoding=[Console]::OutputEncoding=[System.Text.Encoding]::UTF8;' - let &shellredir = '2>&1 | Out-File -Encoding UTF8 %s; exit $LastExitCode' + let &shellredir = '-RedirectStandardOutput %s -NoNewWindow -Wait' let &shellpipe = '2>&1 | Out-File -Encoding UTF8 %s; exit $LastExitCode' set shellquote= shellxquote= diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index c6fcb5140ecabf..2138e94a86253b 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -1571,14 +1571,18 @@ char_u *make_filter_cmd(char_u *cmd, char_u *itmp, char_u *otmp) #else false; #endif + bool is_pwsh = STRNCMP(invocation_path_tail(p_sh, NULL), "pwsh", 4) == 0 + || STRNCMP(invocation_path_tail(p_sh, NULL), "powershell", 10) == 0; size_t len = STRLEN(cmd) + 1; // At least enough space for cmd + NULL. len += is_fish_shell ? sizeof("begin; " "; end") - 1 - : sizeof("(" ")") - 1; + : is_pwsh ? STRLEN("Start-Process ") + : sizeof("(" ")") - 1; if (itmp != NULL) { - len += STRLEN(itmp) + sizeof(" { " " < " " } ") - 1; + len += is_pwsh ? STRLEN(itmp) + STRLEN(" -RedirectStandardInput ") + : STRLEN(itmp) + sizeof(" { " " < " " } ") - 1; } if (otmp != NULL) { len += STRLEN(otmp) + STRLEN(p_srr) + 2; // two extra spaces (" "), @@ -1588,7 +1592,10 @@ char_u *make_filter_cmd(char_u *cmd, char_u *itmp, char_u *otmp) #if defined(UNIX) // Put delimiters around the command (for concatenated commands) when // redirecting input and/or output. - if (itmp != NULL || otmp != NULL) { + if (is_pwsh) { + xstrlcpy(buf, "Start-Process ", len); + xstrlcat(buf, (char *)cmd, len); + } else if (itmp != NULL || otmp != NULL) { char *fmt = is_fish_shell ? "begin; %s; end" : "(%s)"; vim_snprintf(buf, len, fmt, (char *)cmd); @@ -1597,13 +1604,22 @@ char_u *make_filter_cmd(char_u *cmd, char_u *itmp, char_u *otmp) } if (itmp != NULL) { - xstrlcat(buf, " < ", len - 1); + if (is_pwsh) { + xstrlcat(buf, " -RedirectStandardInput ", len - 1); + } else { + xstrlcat(buf, " < ", len - 1); + } xstrlcat(buf, (const char *)itmp, len - 1); } #else // For shells that don't understand braces around commands, at least allow // the use of commands in a pipe. - xstrlcpy(buf, (char *)cmd, len); + if (is_pwsh) { + xstrlcpy(buf, "Start-Process ", len); + xstrlcat(buf, (char *)cmd, len); + } else { + xstrlcpy(buf, (char *)cmd, len); + } if (itmp != NULL) { // If there is a pipe, we have to put the '<' in front of it. // Don't do this when 'shellquote' is not empty, otherwise the @@ -1614,7 +1630,11 @@ char_u *make_filter_cmd(char_u *cmd, char_u *itmp, char_u *otmp) *p = NUL; } } - xstrlcat(buf, " < ", len); + if (is_pwsh) { + xstrlcat(buf, " -RedirectStandardInput ", len); + } else { + xstrlcat(buf, " < ", len); + } xstrlcat(buf, (const char *)itmp, len); if (*p_shq == NUL) { const char *const p = find_pipe((const char *)cmd); diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index 6dc4520953713f..bd897425434eee 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -511,9 +511,9 @@ function module.set_shell_powershell() module.exec([[ let &shell = ']]..shell..[[' set shellquote= shellxquote= - let &shellpipe = '2>&1 | Out-File -Encoding UTF8 %s; exit $LastExitCode' - let &shellredir = '2>&1 | Out-File -Encoding UTF8 %s; exit $LastExitCode' let &shellcmdflag = '-NoLogo -NoProfile -ExecutionPolicy RemoteSigned -Command ]]..cmd..[[' + let &shellpipe = '2>&1 | Out-File -Encoding UTF8 %s; exit $LastExitCode' + let &shellredir = '-RedirectStandardOutput %s -NoNewWindow -Wait' ]]) end diff --git a/test/functional/vimscript/system_spec.lua b/test/functional/vimscript/system_spec.lua index 9cc6424d317077..dd1ac694e77aab 100644 --- a/test/functional/vimscript/system_spec.lua +++ b/test/functional/vimscript/system_spec.lua @@ -6,6 +6,8 @@ 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 insert = helpers.insert +local expect = helpers.expect local exc_exec = helpers.exc_exec local iswin = helpers.iswin local os_kill = helpers.os_kill @@ -630,3 +632,29 @@ describe('systemlist()', function() end) end) + +it(':{range}! works with powershell for filter and redirection #16271', function() + clear() + if not helpers.has_powershell() then + pending("not tested; powershell was not found", function() end) + return + end + local screen = Screen.new(500, 8) + screen:attach() + helpers.set_shell_powershell() + insert([[ + 3 + 1 + 4 + 2]]) + feed(':4verbose %!sort') + screen:expect{ + any=[[Executing command: "Start%-Process sort %-RedirectStandardInput .* %-RedirectStandardOutput .* %-NoNewWindow %-Wait".*4 lines filtered.*Press ENTER or type command to continue]] + } + feed('') + expect([[ + 1 + 2 + 3 + 4]]) +end) From 71e2c49b17c5e1b7e0c72bbbb8bdf6a0e090240a Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 22 Jun 2022 05:51:52 -0700 Subject: [PATCH 2/2] refactor(tests): introduce testprg() Also: - Add a describe('shell :!') section to system_spec. - Make the test for #16271 work on systems without powershell. --- CMakeLists.txt | 2 +- src/nvim/ex_cmds.c | 14 ++-- test/functional/autocmd/termxx_spec.lua | 6 +- test/functional/core/job_spec.lua | 7 +- test/functional/ex_cmds/ls_spec.lua | 4 +- test/functional/ex_cmds/make_spec.lua | 6 +- test/functional/fixtures/CMakeLists.txt | 2 + test/functional/helpers.lua | 25 ++++++- test/functional/terminal/cursor_spec.lua | 4 +- test/functional/terminal/edit_spec.lua | 4 +- test/functional/terminal/ex_terminal_spec.lua | 10 +-- test/functional/terminal/helpers.lua | 4 +- test/functional/terminal/highlight_spec.lua | 8 +- test/functional/terminal/scrollback_spec.lua | 12 +-- test/functional/terminal/tui_spec.lua | 8 +- test/functional/ui/hlstate_spec.lua | 4 +- test/functional/ui/inccommand_spec.lua | 4 +- test/functional/ui/output_spec.lua | 10 +-- test/functional/ui/searchhl_spec.lua | 4 +- test/functional/ui/wildmode_spec.lua | 4 +- test/functional/vimscript/let_spec.lua | 4 +- test/functional/vimscript/system_spec.lua | 74 +++++++++---------- 22 files changed, 116 insertions(+), 104 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1919dde97a8944..769a93aa290161 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -655,7 +655,7 @@ if(BUSTED_PRG) list(APPEND TEST_TARGET_ARGS "USES_TERMINAL") set(UNITTEST_PREREQS nvim-test unittest-headers) - set(FUNCTIONALTEST_PREREQS nvim printenv-test printargs-test shell-test streams-test tty-test ${GENERATED_HELP_TAGS}) + set(FUNCTIONALTEST_PREREQS nvim printenv-test printargs-test shell-test pwsh-test streams-test tty-test ${GENERATED_HELP_TAGS}) set(BENCHMARK_PREREQS nvim tty-test) # Useful for automated build systems, if they want to manually run the tests. diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 2138e94a86253b..08afcbbaae2a5e 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -1577,11 +1577,11 @@ char_u *make_filter_cmd(char_u *cmd, char_u *itmp, char_u *otmp) size_t len = STRLEN(cmd) + 1; // At least enough space for cmd + NULL. len += is_fish_shell ? sizeof("begin; " "; end") - 1 - : is_pwsh ? STRLEN("Start-Process ") + : is_pwsh ? sizeof("Start-Process ") : sizeof("(" ")") - 1; if (itmp != NULL) { - len += is_pwsh ? STRLEN(itmp) + STRLEN(" -RedirectStandardInput ") + len += is_pwsh ? STRLEN(itmp) + sizeof(" -RedirectStandardInput ") : STRLEN(itmp) + sizeof(" { " " < " " } ") - 1; } if (otmp != NULL) { @@ -1609,16 +1609,16 @@ char_u *make_filter_cmd(char_u *cmd, char_u *itmp, char_u *otmp) } else { xstrlcat(buf, " < ", len - 1); } - xstrlcat(buf, (const char *)itmp, len - 1); + xstrlcat(buf, (char *)itmp, len - 1); } #else // For shells that don't understand braces around commands, at least allow // the use of commands in a pipe. if (is_pwsh) { xstrlcpy(buf, "Start-Process ", len); - xstrlcat(buf, (char *)cmd, len); + xstrlcat(buf, cmd, len); } else { - xstrlcpy(buf, (char *)cmd, len); + xstrlcpy(buf, cmd, len); } if (itmp != NULL) { // If there is a pipe, we have to put the '<' in front of it. @@ -1635,9 +1635,9 @@ char_u *make_filter_cmd(char_u *cmd, char_u *itmp, char_u *otmp) } else { xstrlcat(buf, " < ", len); } - xstrlcat(buf, (const char *)itmp, len); + xstrlcat(buf, itmp, len); if (*p_shq == NUL) { - const char *const p = find_pipe((const char *)cmd); + const char *const p = find_pipe(cmd); if (p != NULL) { xstrlcat(buf, " ", len - 1); // Insert a space before the '|' for DOS xstrlcat(buf, p, len - 1); diff --git a/test/functional/autocmd/termxx_spec.lua b/test/functional/autocmd/termxx_spec.lua index 1e8f9814377c97..c418a12fafa26b 100644 --- a/test/functional/autocmd/termxx_spec.lua +++ b/test/functional/autocmd/termxx_spec.lua @@ -1,8 +1,8 @@ local luv = require('luv') local helpers = require('test.functional.helpers')(after_each) -local clear, command, nvim, nvim_dir = - helpers.clear, helpers.command, helpers.nvim, helpers.nvim_dir +local clear, command, nvim, testprg = + helpers.clear, helpers.command, helpers.nvim, helpers.testprg local eval, eq, neq, retry = helpers.eval, helpers.eq, helpers.neq, helpers.retry local ok = helpers.ok @@ -12,7 +12,7 @@ local iswin = helpers.iswin describe('autocmd TermClose', function() before_each(function() clear() - nvim('set_option', 'shell', nvim_dir .. '/shell-test') + nvim('set_option', 'shell', testprg('shell-test')) command('set shellcmdflag=EXE shellredir= shellpipe= shellquote= shellxquote=') end) diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua index cf24e570cb1669..d443e06e09351a 100644 --- a/test/functional/core/job_spec.lua +++ b/test/functional/core/job_spec.lua @@ -1,9 +1,9 @@ local helpers = require('test.functional.helpers')(after_each) local clear, eq, eval, exc_exec, feed_command, feed, insert, neq, next_msg, nvim, - nvim_dir, ok, source, write_file, mkdir, rmdir = helpers.clear, + testprg, ok, source, write_file, mkdir, rmdir = helpers.clear, helpers.eq, helpers.eval, helpers.exc_exec, helpers.feed_command, helpers.feed, helpers.insert, helpers.neq, helpers.next_msg, helpers.nvim, - helpers.nvim_dir, helpers.ok, helpers.source, + helpers.testprg, helpers.ok, helpers.source, helpers.write_file, helpers.mkdir, helpers.rmdir local assert_alive = helpers.assert_alive local command = helpers.command @@ -1042,8 +1042,7 @@ describe('jobs', function() return a:data endfunction ]]) - local ext = iswin() and '.exe' or '' - insert(nvim_dir..'/tty-test'..ext) -- Full path to tty-test. + insert(testprg('tty-test')) nvim('command', 'let g:job_opts.pty = 1') nvim('command', 'let exec = [expand(":p")]') nvim('command', "let j = jobstart(exec, g:job_opts)") diff --git a/test/functional/ex_cmds/ls_spec.lua b/test/functional/ex_cmds/ls_spec.lua index 9853084c47bfe2..2583d802694aa3 100644 --- a/test/functional/ex_cmds/ls_spec.lua +++ b/test/functional/ex_cmds/ls_spec.lua @@ -5,7 +5,7 @@ local eq = helpers.eq local eval = helpers.eval local feed = helpers.feed local nvim = helpers.nvim -local nvim_dir = helpers.nvim_dir +local testprg = helpers.testprg local retry = helpers.retry describe(':ls', function() @@ -14,7 +14,7 @@ describe(':ls', function() end) it('R, F for :terminal buffers', function() - nvim('set_option', 'shell', string.format('"%s" INTERACT', nvim_dir..'/shell-test')) + nvim('set_option', 'shell', string.format('"%s" INTERACT', testprg('shell-test'))) command('edit foo') command('set hidden') diff --git a/test/functional/ex_cmds/make_spec.lua b/test/functional/ex_cmds/make_spec.lua index 3b4d22ab389cec..bf585ee44ca04f 100644 --- a/test/functional/ex_cmds/make_spec.lua +++ b/test/functional/ex_cmds/make_spec.lua @@ -4,7 +4,7 @@ local eval = helpers.eval local has_powershell = helpers.has_powershell local matches = helpers.matches local nvim = helpers.nvim -local nvim_dir = helpers.nvim_dir +local testprg = helpers.testprg describe(':make', function() clear() @@ -22,7 +22,7 @@ describe(':make', function() end) it('captures stderr & non zero exit code #14349', function () - nvim('set_option', 'makeprg', nvim_dir..'/shell-test foo') + nvim('set_option', 'makeprg', testprg('shell-test')..' foo') local out = eval('execute("make")') -- Make program exit code correctly captured matches('\nshell returned 3', out) @@ -31,7 +31,7 @@ describe(':make', function() end) it('captures stderr & zero exit code #14349', function () - nvim('set_option', 'makeprg', nvim_dir..'/shell-test') + nvim('set_option', 'makeprg', testprg('shell-test')) local out = eval('execute("make")') -- Ensure there are no "shell returned X" messages between -- command and last line (indicating zero exit) diff --git a/test/functional/fixtures/CMakeLists.txt b/test/functional/fixtures/CMakeLists.txt index 270540de2ecf58..a197e8c2b996ca 100644 --- a/test/functional/fixtures/CMakeLists.txt +++ b/test/functional/fixtures/CMakeLists.txt @@ -2,6 +2,8 @@ add_executable(tty-test EXCLUDE_FROM_ALL tty-test.c) target_link_libraries(tty-test ${LIBUV_LIBRARIES}) add_executable(shell-test EXCLUDE_FROM_ALL shell-test.c) +# Fake pwsh (powershell) for testing make_filter_cmd(). #16271 +add_executable(pwsh-test EXCLUDE_FROM_ALL shell-test.c) add_executable(printargs-test EXCLUDE_FROM_ALL printargs-test.c) add_executable(printenv-test EXCLUDE_FROM_ALL printenv-test.c) if(WIN32) diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index bd897425434eee..6cc0bae5129024 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -501,9 +501,17 @@ function module.has_powershell() return module.eval('executable("'..(iswin() and 'powershell' or 'pwsh')..'")') == 1 end -function module.set_shell_powershell() - local shell = iswin() and 'powershell' or 'pwsh' - assert(module.has_powershell()) +--- Sets Nvim shell to powershell. +--- +--- @param fake (boolean) If true, a fake will be used if powershell is not +--- found on the system. +--- @returns true if powershell was found on the system, else false. +function module.set_shell_powershell(fake) + local found = module.has_powershell() + if not fake then + assert(found) + end + local shell = found and (iswin() and 'powershell' or 'pwsh') or module.testprg('pwsh-test') local set_encoding = '[Console]::InputEncoding=[Console]::OutputEncoding=[System.Text.Encoding]::UTF8;' local cmd = set_encoding..'Remove-Item -Force '..table.concat(iswin() and {'alias:cat', 'alias:echo', 'alias:sleep'} @@ -515,6 +523,7 @@ function module.set_shell_powershell() let &shellpipe = '2>&1 | Out-File -Encoding UTF8 %s; exit $LastExitCode' let &shellredir = '-RedirectStandardOutput %s -NoNewWindow -Wait' ]]) + return found end function module.nvim(method, ...) @@ -774,11 +783,21 @@ function module.get_pathsep() return iswin() and '\\' or '/' end +--- Gets the filesystem root dir, namely "/" or "C:/". function module.pathroot() local pathsep = package.config:sub(1,1) return iswin() and (module.nvim_dir:sub(1,2)..pathsep) or '/' end +--- Gets the full `…/build/bin/{name}` path of a test program produced by +--- `test/functional/fixtures/CMakeLists.txt`. +--- +--- @param name (string) Name of the test program. +function module.testprg(name) + local ext = module.iswin() and '.exe' or '' + return ('%s/%s%s'):format(module.nvim_dir, name, ext) +end + -- Returns a valid, platform-independent Nvim listen address. -- Useful for communicating with child instances. function module.new_pipename() diff --git a/test/functional/terminal/cursor_spec.lua b/test/functional/terminal/cursor_spec.lua index 3b905f1f56f200..e12ab8dae0ad9c 100644 --- a/test/functional/terminal/cursor_spec.lua +++ b/test/functional/terminal/cursor_spec.lua @@ -2,7 +2,7 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local thelpers = require('test.functional.terminal.helpers') local feed, clear, nvim = helpers.feed, helpers.clear, helpers.nvim -local nvim_dir, command = helpers.nvim_dir, helpers.command +local testprg, command = helpers.testprg, helpers.command local nvim_prog = helpers.nvim_prog local eq, eval = helpers.eq, helpers.eval local feed_command = helpers.feed_command @@ -149,7 +149,7 @@ describe('cursor with customized highlighting', function() [3] = {bold = true}, }) screen:attach({rgb=false}) - command('call termopen(["'..nvim_dir..'/tty-test"])') + command('call termopen(["'..testprg('tty-test')..'"])') feed_command('startinsert') end) diff --git a/test/functional/terminal/edit_spec.lua b/test/functional/terminal/edit_spec.lua index e7025d6739bb60..aeb4b7cc2e77e3 100644 --- a/test/functional/terminal/edit_spec.lua +++ b/test/functional/terminal/edit_spec.lua @@ -3,7 +3,7 @@ local screen = require('test.functional.ui.screen') local curbufmeths = helpers.curbufmeths local curwinmeths = helpers.curwinmeths -local nvim_dir = helpers.nvim_dir +local testprg = helpers.testprg local command = helpers.command local funcs = helpers.funcs local meths = helpers.meths @@ -21,7 +21,7 @@ describe(':edit term://*', function() before_each(function() clear() - meths.set_option('shell', nvim_dir .. '/shell-test') + meths.set_option('shell', testprg('shell-test')) meths.set_option('shellcmdflag', 'EXE') end) diff --git a/test/functional/terminal/ex_terminal_spec.lua b/test/functional/terminal/ex_terminal_spec.lua index b4f29a586aa1aa..23b69319f015b6 100644 --- a/test/functional/terminal/ex_terminal_spec.lua +++ b/test/functional/terminal/ex_terminal_spec.lua @@ -2,7 +2,7 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local assert_alive = helpers.assert_alive local clear, poke_eventloop, nvim = helpers.clear, helpers.poke_eventloop, helpers.nvim -local nvim_dir, source, eq = helpers.nvim_dir, helpers.source, helpers.eq +local testprg, source, eq = helpers.testprg, helpers.source, helpers.eq local feed = helpers.feed local feed_command, eval = helpers.feed_command, helpers.eval local funcs = helpers.funcs @@ -28,7 +28,7 @@ describe(':terminal', function() echomsg "msg3" ]]) -- Invoke a command that emits frequent terminal activity. - feed([[:terminal "]]..nvim_dir..[[/shell-test" REP 9999 !terminal_output!]]) + feed([[:terminal "]]..testprg('shell-test')..[[" REP 9999 !terminal_output!]]) feed([[]]) poke_eventloop() -- Wait for some terminal activity. @@ -131,7 +131,7 @@ describe(':terminal (with fake shell)', function() screen = Screen.new(50, 4) screen:attach({rgb=false}) -- shell-test.c is a fake shell that prints its arguments and exits. - nvim('set_option', 'shell', nvim_dir..'/shell-test') + nvim('set_option', 'shell', testprg('shell-test')) nvim('set_option', 'shellcmdflag', 'EXE') end) @@ -167,7 +167,7 @@ describe(':terminal (with fake shell)', function() it("with no argument, but 'shell' has arguments, acts like termopen()", function() if helpers.pending_win32(pending) then return end - nvim('set_option', 'shell', nvim_dir..'/shell-test -t jeff') + nvim('set_option', 'shell', testprg('shell-test')..' -t jeff') terminal_with_fake_shell() screen:expect([[ ^jeff $ | @@ -191,7 +191,7 @@ describe(':terminal (with fake shell)', function() it("executes a given command through the shell, when 'shell' has arguments", function() if helpers.pending_win32(pending) then return end - nvim('set_option', 'shell', nvim_dir..'/shell-test -t jeff') + nvim('set_option', 'shell', testprg('shell-test')..' -t jeff') command('set shellxquote=') -- win: avoid extra quotes terminal_with_fake_shell('echo hi') screen:expect([[ diff --git a/test/functional/terminal/helpers.lua b/test/functional/terminal/helpers.lua index 51ecae663afcb4..bcfd3559e6022d 100644 --- a/test/functional/terminal/helpers.lua +++ b/test/functional/terminal/helpers.lua @@ -3,7 +3,7 @@ -- operate on the _host_ session, _not_ the child session. local helpers = require('test.functional.helpers')(nil) local Screen = require('test.functional.ui.screen') -local nvim_dir = helpers.nvim_dir +local testprg = helpers.testprg local feed_command, nvim = helpers.feed_command, helpers.nvim local function feed_data(data) @@ -37,7 +37,7 @@ local function clear_attrs() feed_termcode('[0;10m') end local function enable_mouse() feed_termcode('[?1002h') end local function disable_mouse() feed_termcode('[?1002l') end -local default_command = '["'..nvim_dir..'/tty-test'..'"]' +local default_command = '["'..testprg('tty-test')..'"]' local function screen_setup(extra_rows, command, cols, opts) extra_rows = extra_rows and extra_rows or 0 diff --git a/test/functional/terminal/highlight_spec.lua b/test/functional/terminal/highlight_spec.lua index 32c911a5e8c89c..02af53d7ac74ec 100644 --- a/test/functional/terminal/highlight_spec.lua +++ b/test/functional/terminal/highlight_spec.lua @@ -2,7 +2,7 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local thelpers = require('test.functional.terminal.helpers') local feed, clear, nvim = helpers.feed, helpers.clear, helpers.nvim -local nvim_dir, command = helpers.nvim_dir, helpers.command +local testprg, command = helpers.testprg, helpers.command local nvim_prog_abs = helpers.nvim_prog_abs local eq, eval = helpers.eq, helpers.eval local funcs = helpers.funcs @@ -28,7 +28,7 @@ describe(':terminal highlight', function() [11] = {background = 11}, }) screen:attach({rgb=false}) - command('enew | call termopen(["'..nvim_dir..'/tty-test"])') + command(("enew | call termopen(['%s'])"):format(testprg('tty-test'))) feed('i') screen:expect([[ tty ready | @@ -174,7 +174,7 @@ describe(':terminal highlight forwarding', function() [4] = {{foreground = tonumber('0xff8000')}, {}}, }) screen:attach() - command('enew | call termopen(["'..nvim_dir..'/tty-test"])') + command(("enew | call termopen(['%s'])"):format(testprg('tty-test'))) feed('i') screen:expect([[ tty ready | @@ -226,7 +226,7 @@ describe(':terminal highlight with custom palette', function() }) screen:attach({rgb=true}) nvim('set_var', 'terminal_color_3', '#123456') - command('enew | call termopen(["'..nvim_dir..'/tty-test"])') + command(("enew | call termopen(['%s'])"):format(testprg('tty-test'))) feed('i') screen:expect([[ tty ready | diff --git a/test/functional/terminal/scrollback_spec.lua b/test/functional/terminal/scrollback_spec.lua index d1cfc7e91b6768..8b8fd1546e15f5 100644 --- a/test/functional/terminal/scrollback_spec.lua +++ b/test/functional/terminal/scrollback_spec.lua @@ -2,7 +2,7 @@ local Screen = require('test.functional.ui.screen') local helpers = require('test.functional.helpers')(after_each) local thelpers = require('test.functional.terminal.helpers') local clear, eq, curbuf = helpers.clear, helpers.eq, helpers.curbuf -local feed, nvim_dir, feed_command = helpers.feed, helpers.nvim_dir, helpers.feed_command +local feed, testprg, feed_command = helpers.feed, helpers.testprg, helpers.feed_command local iswin = helpers.iswin local eval = helpers.eval local command = helpers.command @@ -349,7 +349,7 @@ describe(':terminal prints more lines than the screen height and exits', functio clear() local screen = Screen.new(30, 7) screen:attach({rgb=false}) - feed_command('call termopen(["'..nvim_dir..'/tty-test", "10"]) | startinsert') + feed_command(("call termopen(['%s', '10']) | startinsert"):format(testprg('tty-test'))) poke_eventloop() screen:expect([[ line6 | @@ -381,7 +381,7 @@ describe("'scrollback' option", function() local function set_fake_shell() -- shell-test.c is a fake shell that prints its arguments and exits. - nvim('set_option', 'shell', nvim_dir..'/shell-test') + nvim('set_option', 'shell', testprg('shell-test')) nvim('set_option', 'shellcmdflag', 'EXE') end @@ -402,7 +402,7 @@ describe("'scrollback' option", function() end curbufmeths.set_option('scrollback', 0) - feed_data(nvim_dir..'/shell-test REP 31 line'..(iswin() and '\r' or '\n')) + feed_data(('%s REP 31 line%s'):format(testprg('shell-test'), iswin() and '\r' or '\n')) screen:expect{any='30: line '} retry(nil, nil, function() expect_lines(7) end) end) @@ -422,7 +422,7 @@ describe("'scrollback' option", function() -- Wait for prompt. screen:expect{any='%$'} - feed_data(nvim_dir.."/shell-test REP 31 line"..(iswin() and '\r' or '\n')) + feed_data(('%s REP 31 line%s'):format(testprg('shell-test'), iswin() and '\r' or '\n')) screen:expect{any='30: line '} retry(nil, nil, function() expect_lines(33, 2) end) @@ -435,7 +435,7 @@ describe("'scrollback' option", function() -- 'scrollback' option is synchronized with the internal sb_buffer. command('sleep 100m') - feed_data(nvim_dir.."/shell-test REP 41 line"..(iswin() and '\r' or '\n')) + feed_data(('%s REP 41 line%s'):format(testprg('shell-test'), iswin() and '\r' or '\n')) if iswin() then screen:expect{grid=[[ 37: line | diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index 8c6cba4def6412..89704be82007f6 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -14,7 +14,7 @@ local feed_command = helpers.feed_command local feed_data = thelpers.feed_data local clear = helpers.clear local command = helpers.command -local nvim_dir = helpers.nvim_dir +local testprg = helpers.testprg local retry = helpers.retry local nvim_prog = helpers.nvim_prog local nvim_set = helpers.nvim_set @@ -385,7 +385,7 @@ describe('TUI', function() return end feed_data(':set statusline=^^^^^^^\n') - feed_data(':terminal '..nvim_dir..'/tty-test\n') + feed_data(':terminal '..testprg('tty-test')..'\n') feed_data('i') screen:expect{grid=[[ tty ready | @@ -903,7 +903,7 @@ describe('TUI', function() feed_data(':set statusline=^^^^^^^\n') feed_data(':set termguicolors\n') - feed_data(':terminal '..nvim_dir..'/tty-test\n') + feed_data(':terminal '..testprg('tty-test')..'\n') -- Depending on platform the above might or might not fit in the cmdline -- so clear it for consistent behavior. feed_data(':\027') @@ -1130,7 +1130,7 @@ describe('TUI FocusGained/FocusLost', function() end) it('in terminal-mode', function() - feed_data(':set shell='..nvim_dir..'/shell-test\n') + feed_data(':set shell='..testprg('shell-test')..'\n') feed_data(':set noshowmode laststatus=0\n') feed_data(':terminal\n') diff --git a/test/functional/ui/hlstate_spec.lua b/test/functional/ui/hlstate_spec.lua index dcea2c76ddb202..5b8619fe92bb72 100644 --- a/test/functional/ui/hlstate_spec.lua +++ b/test/functional/ui/hlstate_spec.lua @@ -5,7 +5,7 @@ local clear, insert = helpers.clear, helpers.insert local command = helpers.command local meths = helpers.meths local iswin = helpers.iswin -local nvim_dir = helpers.nvim_dir +local testprg = helpers.testprg local thelpers = require('test.functional.terminal.helpers') describe('ext_hlstate detailed highlights', function() @@ -190,7 +190,7 @@ describe('ext_hlstate detailed highlights', function() [6] = {{foreground = tonumber('0x40ffff'), fg_indexed=true}, {5, 1}}, [7] = {{}, {{hi_name = "MsgArea", ui_name = "MsgArea", kind = "ui"}}}, }) - command('enew | call termopen(["'..nvim_dir..'/tty-test"])') + command(("enew | call termopen(['%s'])"):format(testprg('tty-test'))) screen:expect([[ ^tty ready | {1: } | diff --git a/test/functional/ui/inccommand_spec.lua b/test/functional/ui/inccommand_spec.lua index 10700d950803da..aebe956d8b1af3 100644 --- a/test/functional/ui/inccommand_spec.lua +++ b/test/functional/ui/inccommand_spec.lua @@ -17,7 +17,7 @@ local source = helpers.source local poke_eventloop = helpers.poke_eventloop local nvim = helpers.nvim local sleep = helpers.sleep -local nvim_dir = helpers.nvim_dir +local testprg = helpers.testprg local assert_alive = helpers.assert_alive local default_text = [[ @@ -2795,7 +2795,7 @@ it(':substitute with inccommand during :terminal activity', function() clear() command("set cmdwinheight=3") - feed([[:terminal "]]..nvim_dir..[[/shell-test" REP 5000 xxx]]) + feed(([[:terminal "%s" REP 5000 xxx]]):format(testprg('shell-test'))) command('file term') feed('G') -- Follow :terminal output. command('new') diff --git a/test/functional/ui/output_spec.lua b/test/functional/ui/output_spec.lua index 7305baa761795d..f2bbf2099fb401 100644 --- a/test/functional/ui/output_spec.lua +++ b/test/functional/ui/output_spec.lua @@ -9,6 +9,7 @@ local feed_command = helpers.feed_command local iswin = helpers.iswin local clear = helpers.clear local command = helpers.command +local testprg = helpers.testprg local nvim_dir = helpers.nvim_dir local has_powershell = helpers.has_powershell local set_shell_powershell = helpers.set_shell_powershell @@ -55,7 +56,7 @@ describe("shell command :!", function() if 'openbsd' == helpers.uname() then pending('FIXME #10804') end - child_session.feed_data(":!"..nvim_dir.."/shell-test REP 30001 foo\n") + child_session.feed_data((":!%s REP 30001 foo\n"):format(testprg('shell-test'))) -- If we observe any line starting with a dot, then throttling occurred. -- Avoid false failure on slow systems. @@ -208,12 +209,7 @@ describe("shell command :!", function() it('handles multibyte sequences split over buffer boundaries', function() command('cd '..nvim_dir) - local cmd - if iswin() then - cmd = '!shell-test UTF-8 ' - else - cmd = '!./shell-test UTF-8' - end + local cmd = iswin() and '!shell-test UTF-8 ' or '!./shell-test UTF-8' feed_command(cmd) -- Note: only the first example of split composed char works screen:expect([[ diff --git a/test/functional/ui/searchhl_spec.lua b/test/functional/ui/searchhl_spec.lua index 2e60930127bb39..8c3813cd1c513c 100644 --- a/test/functional/ui/searchhl_spec.lua +++ b/test/functional/ui/searchhl_spec.lua @@ -5,7 +5,7 @@ local command = helpers.command local feed_command = helpers.feed_command local eq = helpers.eq local eval = helpers.eval -local nvim_dir = helpers.nvim_dir +local testprg = helpers.testprg describe('search highlighting', function() local screen @@ -163,7 +163,7 @@ describe('search highlighting', function() end) it('is preserved during :terminal activity', function() - feed([[:terminal "]]..nvim_dir..[[/shell-test" REP 5000 foo]]) + feed((':terminal "%s" REP 5000 foo'):format(testprg('shell-test'))) feed(':file term') feed('G') -- Follow :terminal output. diff --git a/test/functional/ui/wildmode_spec.lua b/test/functional/ui/wildmode_spec.lua index 65c6fabfa8dab3..98398bc7a1256a 100644 --- a/test/functional/ui/wildmode_spec.lua +++ b/test/functional/ui/wildmode_spec.lua @@ -7,7 +7,7 @@ local meths = helpers.meths local eq = helpers.eq local eval = helpers.eval local retry = helpers.retry -local nvim_dir = helpers.nvim_dir +local testprg = helpers.testprg describe("'wildmenu'", function() local screen @@ -114,7 +114,7 @@ describe("'wildmenu'", function() it('is preserved during :terminal activity', function() command('set wildmenu wildmode=full') command('set scrollback=4') - feed([[:terminal "]]..nvim_dir..[[/shell-test" REP 5000 !terminal_output!]]) + feed((':terminal "%s" REP 5000 !terminal_output!'):format(testprg('shell-test'))) feed('G') -- Follow :terminal output. feed([[:sign ]]) -- Invoke wildmenu. -- NB: in earlier versions terminal output was redrawn during cmdline mode. diff --git a/test/functional/vimscript/let_spec.lua b/test/functional/vimscript/let_spec.lua index 4ff4090a186274..509846450c7d68 100644 --- a/test/functional/vimscript/let_spec.lua +++ b/test/functional/vimscript/let_spec.lua @@ -7,7 +7,7 @@ local eval = helpers.eval local meths = helpers.meths local exec_capture = helpers.exec_capture local source = helpers.source -local nvim_dir = helpers.nvim_dir +local testprg = helpers.testprg before_each(clear) @@ -59,7 +59,7 @@ describe(':let', function() end) it("multibyte env var to child process #8398 #9267", function() - local cmd_get_child_env = "let g:env_from_child = system(['"..nvim_dir.."/printenv-test', 'NVIM_TEST'])" + local cmd_get_child_env = ("let g:env_from_child = system(['%s', 'NVIM_TEST'])"):format(testprg('printenv-test')) command("let $NVIM_TEST = 'AìaB'") command(cmd_get_child_env) eq(eval('$NVIM_TEST'), eval('g:env_from_child')) diff --git a/test/functional/vimscript/system_spec.lua b/test/functional/vimscript/system_spec.lua index dd1ac694e77aab..49b48fecd9aa67 100644 --- a/test/functional/vimscript/system_spec.lua +++ b/test/functional/vimscript/system_spec.lua @@ -1,7 +1,7 @@ local helpers = require('test.functional.helpers')(after_each) local assert_alive = helpers.assert_alive -local nvim_dir = helpers.nvim_dir +local testprg = helpers.testprg local eq, call, clear, eval, feed_command, feed, nvim = helpers.eq, helpers.call, helpers.clear, helpers.eval, helpers.feed_command, helpers.feed, helpers.nvim @@ -32,10 +32,6 @@ describe('system()', function() before_each(clear) describe('command passed as a List', function() - local function printargs_path() - return nvim_dir..'/printargs-test' .. (iswin() and '.exe' or '') - end - it('throws error if cmd[0] is not executable', function() eq("Vim:E475: Invalid value for argument cmd: 'this-should-not-exist' is not executable", pcall_err(call, 'system', { 'this-should-not-exist' })) @@ -68,16 +64,16 @@ describe('system()', function() it('quotes arguments correctly #5280', function() local out = call('system', - { printargs_path(), [[1]], [[2 "3]], [[4 ' 5]], [[6 ' 7']] }) + { testprg('printargs-test'), [[1]], [[2 "3]], [[4 ' 5]], [[6 ' 7']] }) eq(0, eval('v:shell_error')) eq([[arg1=1;arg2=2 "3;arg3=4 ' 5;arg4=6 ' 7';]], out) - out = call('system', { printargs_path(), [['1]], [[2 "3]] }) + out = call('system', { testprg('printargs-test'), [['1]], [[2 "3]] }) eq(0, eval('v:shell_error')) eq([[arg1='1;arg2=2 "3;]], out) - out = call('system', { printargs_path(), "A\nB" }) + out = call('system', { testprg('printargs-test'), "A\nB" }) eq(0, eval('v:shell_error')) eq("arg1=A\nB;", out) end) @@ -169,7 +165,7 @@ describe('system()', function() end end) - it('works with powershell', function() + it('with powershell', function() helpers.set_shell_powershell() eq('a\nb\n', eval([[system('Write-Output a b')]])) eq('C:\\\n', eval([[system('cd c:\; (Get-Location).Path')]])) @@ -177,12 +173,11 @@ describe('system()', function() end) end - it('works with powershell w/ UTF-8 text (#13713)', function() + it('powershell w/ UTF-8 text #13713', function() if not helpers.has_powershell() then pending("not tested; powershell was not found", function() end) return end - -- Should work with recommended config used in helper helpers.set_shell_powershell() eq('ああ\n', eval([[system('Write-Output "ああ"')]])) -- Sanity test w/ default encoding @@ -432,7 +427,7 @@ describe('system()', function() end) it("with a program that doesn't close stdout will exit properly after passing input", function() - local out = eval(string.format("system('%s', 'clip-data')", nvim_dir..'/streams-test')) + local out = eval(string.format("system('%s', 'clip-data')", testprg('streams-test'))) assert(out:sub(0, 5) == 'pid: ', out) os_kill(out:match("%d+")) end) @@ -611,17 +606,16 @@ describe('systemlist()', function() end) it("with a program that doesn't close stdout will exit properly after passing input", function() - local out = eval(string.format("systemlist('%s', 'clip-data')", nvim_dir..'/streams-test')) + local out = eval(string.format("systemlist('%s', 'clip-data')", testprg('streams-test'))) assert(out[1]:sub(0, 5) == 'pid: ', out) os_kill(out[1]:match("%d+")) end) - it('works with powershell w/ UTF-8 text (#13713)', function() + it('powershell w/ UTF-8 text #13713', function() if not helpers.has_powershell() then pending("not tested; powershell was not found", function() end) return end - -- Should work with recommended config used in helper helpers.set_shell_powershell() eq({iswin() and 'あ\r' or 'あ'}, eval([[systemlist('Write-Output あ')]])) -- Sanity test w/ default encoding @@ -633,28 +627,30 @@ describe('systemlist()', function() end) -it(':{range}! works with powershell for filter and redirection #16271', function() - clear() - if not helpers.has_powershell() then - pending("not tested; powershell was not found", function() end) - return - end - local screen = Screen.new(500, 8) - screen:attach() - helpers.set_shell_powershell() - insert([[ - 3 - 1 - 4 - 2]]) - feed(':4verbose %!sort') - screen:expect{ - any=[[Executing command: "Start%-Process sort %-RedirectStandardInput .* %-RedirectStandardOutput .* %-NoNewWindow %-Wait".*4 lines filtered.*Press ENTER or type command to continue]] - } - feed('') - expect([[ - 1 - 2 - 3 - 4]]) +describe('shell :!', function() + before_each(clear) + + it(':{range}! with powershell filter/redirect #16271', function() + local screen = Screen.new(500, 8) + screen:attach() + local found = helpers.set_shell_powershell(true) + insert([[ + 3 + 1 + 4 + 2]]) + feed(':4verbose %!sort') + screen:expect{ + any=[[Executing command: .?Start%-Process sort %-RedirectStandardInput .* %-RedirectStandardOutput .* %-NoNewWindow %-Wait]] + } + feed('') + if found then + -- Not using fake powershell, so we can test the result. + expect([[ + 1 + 2 + 3 + 4]]) + end + end) end)