system_spec.lua 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627
  1. -- Tests for system() and :! shell.
  2. local t = require('test.testutil')
  3. local n = require('test.functional.testnvim')()
  4. local Screen = require('test.functional.ui.screen')
  5. local assert_alive = n.assert_alive
  6. local testprg = n.testprg
  7. local eq, call, clear, eval, feed_command, feed, api =
  8. t.eq, n.call, n.clear, n.eval, n.feed_command, n.feed, n.api
  9. local command = n.command
  10. local insert = n.insert
  11. local expect = n.expect
  12. local exc_exec = n.exc_exec
  13. local os_kill = n.os_kill
  14. local pcall_err = t.pcall_err
  15. local is_os = t.is_os
  16. local function create_file_with_nuls(name)
  17. return function()
  18. feed('ipart1<C-V>000part2<C-V>000part3<ESC>:w ' .. name .. '<CR>')
  19. eval('1') -- wait for the file to be created
  20. end
  21. end
  22. local function delete_file(name)
  23. return function()
  24. eval("delete('" .. name .. "')")
  25. end
  26. end
  27. describe('system()', function()
  28. before_each(clear)
  29. describe('command passed as a List', function()
  30. it('throws error if cmd[0] is not executable', function()
  31. eq(
  32. "Vim:E475: Invalid value for argument cmd: 'this-should-not-exist' is not executable",
  33. pcall_err(call, 'system', { 'this-should-not-exist' })
  34. )
  35. eq(-1, eval('v:shell_error'))
  36. end)
  37. it('parameter validation does NOT modify v:shell_error', function()
  38. -- 1. Call system() with invalid parameters.
  39. -- 2. Assert that v:shell_error was NOT set.
  40. feed_command('call system({})')
  41. eq('E475: Invalid argument: expected String or List', eval('v:errmsg'))
  42. eq(0, eval('v:shell_error'))
  43. feed_command('call system([])')
  44. eq('E474: Invalid argument', eval('v:errmsg'))
  45. eq(0, eval('v:shell_error'))
  46. -- Provoke a non-zero v:shell_error.
  47. eq(
  48. "Vim:E475: Invalid value for argument cmd: 'this-should-not-exist' is not executable",
  49. pcall_err(call, 'system', { 'this-should-not-exist' })
  50. )
  51. local old_val = eval('v:shell_error')
  52. eq(-1, old_val)
  53. -- 1. Call system() with invalid parameters.
  54. -- 2. Assert that v:shell_error was NOT modified.
  55. feed_command('call system({})')
  56. eq(old_val, eval('v:shell_error'))
  57. feed_command('call system([])')
  58. eq(old_val, eval('v:shell_error'))
  59. end)
  60. it('quotes arguments correctly #5280', function()
  61. local out =
  62. call('system', { testprg('printargs-test'), [[1]], [[2 "3]], [[4 ' 5]], [[6 ' 7']] })
  63. eq(0, eval('v:shell_error'))
  64. eq([[arg1=1;arg2=2 "3;arg3=4 ' 5;arg4=6 ' 7';]], out)
  65. out = call('system', { testprg('printargs-test'), [['1]], [[2 "3]] })
  66. eq(0, eval('v:shell_error'))
  67. eq([[arg1='1;arg2=2 "3;]], out)
  68. out = call('system', { testprg('printargs-test'), 'A\nB' })
  69. eq(0, eval('v:shell_error'))
  70. eq('arg1=A\nB;', out)
  71. end)
  72. it('calls executable in $PATH', function()
  73. if 0 == eval("executable('python3')") then
  74. pending('missing `python3`')
  75. end
  76. eq('foo\n', eval([[system(['python3', '-c', 'print("foo")'])]]))
  77. eq(0, eval('v:shell_error'))
  78. end)
  79. it('does NOT run in shell', function()
  80. if is_os('win') then
  81. eq(
  82. '%PATH%\n',
  83. eval(
  84. "system(['powershell', '-NoProfile', '-NoLogo', '-ExecutionPolicy', 'RemoteSigned', '-Command', 'Write-Output', '%PATH%'])"
  85. )
  86. )
  87. else
  88. eq('* $PATH %PATH%\n', eval("system(['echo', '*', '$PATH', '%PATH%'])"))
  89. end
  90. end)
  91. end)
  92. it('sets v:shell_error', function()
  93. if is_os('win') then
  94. eval([[system("cmd.exe /c exit")]])
  95. eq(0, eval('v:shell_error'))
  96. eval([[system("cmd.exe /c exit 1")]])
  97. eq(1, eval('v:shell_error'))
  98. eval([[system("cmd.exe /c exit 5")]])
  99. eq(5, eval('v:shell_error'))
  100. eval([[system('this-should-not-exist')]])
  101. eq(1, eval('v:shell_error'))
  102. else
  103. eval([[system("sh -c 'exit'")]])
  104. eq(0, eval('v:shell_error'))
  105. eval([[system("sh -c 'exit 1'")]])
  106. eq(1, eval('v:shell_error'))
  107. eval([[system("sh -c 'exit 5'")]])
  108. eq(5, eval('v:shell_error'))
  109. eval([[system('this-should-not-exist')]])
  110. eq(127, eval('v:shell_error'))
  111. end
  112. end)
  113. describe('executes shell function', function()
  114. local screen
  115. before_each(function()
  116. screen = Screen.new()
  117. screen:attach()
  118. end)
  119. if is_os('win') then
  120. local function test_more()
  121. eq('root = true', eval([[get(split(system('"more" ".editorconfig"'), "\n"), 0, '')]]))
  122. end
  123. local function test_shell_unquoting()
  124. eval([[system('"ping" "-n" "1" "127.0.0.1"')]])
  125. eq(0, eval('v:shell_error'))
  126. eq('"a b"\n', eval([[system('cmd /s/c "cmd /s/c "cmd /s/c "echo "a b""""')]]))
  127. eq(
  128. '"a b"\n',
  129. eval(
  130. [[system('powershell -NoProfile -NoLogo -ExecutionPolicy RemoteSigned -Command Write-Output ''\^"a b\^"''')]]
  131. )
  132. )
  133. end
  134. it('with shell=cmd.exe', function()
  135. command('set shell=cmd.exe')
  136. eq('""\n', eval([[system('echo ""')]]))
  137. eq('"a b"\n', eval([[system('echo "a b"')]]))
  138. eq('a \nb\n', eval([[system('echo a & echo b')]]))
  139. eq('a \n', eval([[system('echo a 2>&1')]]))
  140. test_more()
  141. eval([[system('cd "C:\Program Files"')]])
  142. eq(0, eval('v:shell_error'))
  143. test_shell_unquoting()
  144. end)
  145. it('with shell=cmd', function()
  146. command('set shell=cmd')
  147. eq('"a b"\n', eval([[system('echo "a b"')]]))
  148. test_more()
  149. test_shell_unquoting()
  150. end)
  151. it('with shell=$COMSPEC', function()
  152. local comspecshell = eval("fnamemodify($COMSPEC, ':t')")
  153. if comspecshell == 'cmd.exe' then
  154. command('set shell=$COMSPEC')
  155. eq('"a b"\n', eval([[system('echo "a b"')]]))
  156. test_more()
  157. test_shell_unquoting()
  158. else
  159. pending('$COMSPEC is not cmd.exe: ' .. comspecshell)
  160. end
  161. end)
  162. it('with powershell', function()
  163. n.set_shell_powershell()
  164. eq('a\nb\n', eval([[system('Write-Output a b')]]))
  165. eq('C:\\\n', eval([[system('cd c:\; (Get-Location).Path')]]))
  166. eq('a b\n', eval([[system('Write-Output "a b"')]]))
  167. end)
  168. end
  169. it('powershell w/ UTF-8 text #13713', function()
  170. if not n.has_powershell() then
  171. pending('powershell not found', function() end)
  172. return
  173. end
  174. n.set_shell_powershell()
  175. eq('ああ\n', eval([[system('Write-Output "ああ"')]]))
  176. -- Sanity test w/ default encoding
  177. -- * on Windows, expected to default to Western European enc
  178. -- * on Linux, expected to default to UTF8
  179. command([[let &shellcmdflag = '-NoLogo -NoProfile -ExecutionPolicy RemoteSigned -Command ']])
  180. eq(is_os('win') and '??\n' or 'ああ\n', eval([[system('Write-Output "ああ"')]]))
  181. end)
  182. it('`echo` and waits for its return', function()
  183. feed(':call system("echo")<cr>')
  184. screen:expect([[
  185. ^ |
  186. {1:~ }|*12
  187. :call system("echo") |
  188. ]])
  189. end)
  190. it('prints verbose information', function()
  191. api.nvim_set_option_value('shell', 'fake_shell', {})
  192. api.nvim_set_option_value('shellcmdflag', 'cmdflag', {})
  193. screen:try_resize(72, 14)
  194. feed(':4verbose echo system("echo hi")<cr>')
  195. if is_os('win') then
  196. screen:expect { any = [[Executing command: "'fake_shell' 'cmdflag' '"echo hi"'"]] }
  197. else
  198. screen:expect { any = [[Executing command: "'fake_shell' 'cmdflag' 'echo hi'"]] }
  199. end
  200. feed('<cr>')
  201. end)
  202. it('self and total time recorded separately', function()
  203. local tempfile = t.tmpname()
  204. feed(':function! AlmostNoSelfTime()<cr>')
  205. feed('echo system("echo hi")<cr>')
  206. feed('endfunction<cr>')
  207. feed(':profile start ' .. tempfile .. '<cr>')
  208. feed(':profile func AlmostNoSelfTime<cr>')
  209. feed(':call AlmostNoSelfTime()<cr>')
  210. feed(':profile dump<cr>')
  211. feed(':edit ' .. tempfile .. '<cr>')
  212. local command_total_time = tonumber(n.fn.split(n.fn.getline(7))[2])
  213. local command_self_time = tonumber(n.fn.split(n.fn.getline(7))[3])
  214. t.neq(nil, command_total_time)
  215. t.neq(nil, command_self_time)
  216. end)
  217. it('`yes` interrupted with CTRL-C', function()
  218. feed(
  219. ':call system("'
  220. .. (is_os('win') and 'for /L %I in (1,0,2) do @echo y' or 'yes')
  221. .. '")<cr>'
  222. )
  223. screen:expect([[
  224. |
  225. {1:~ }|*12
  226. ]] .. (is_os('win') and [[
  227. :call system("for /L %I in (1,0,2) do @echo y") |]] or [[
  228. :call system("yes") |]]))
  229. feed('foo<c-c>')
  230. screen:expect([[
  231. ^ |
  232. {1:~ }|*12
  233. Type :qa and press <Enter> to exit Nvim |
  234. ]])
  235. end)
  236. it('`yes` interrupted with mapped CTRL-C', function()
  237. command('nnoremap <C-C> i')
  238. feed(
  239. ':call system("'
  240. .. (is_os('win') and 'for /L %I in (1,0,2) do @echo y' or 'yes')
  241. .. '")<cr>'
  242. )
  243. screen:expect([[
  244. |
  245. {1:~ }|*12
  246. ]] .. (is_os('win') and [[
  247. :call system("for /L %I in (1,0,2) do @echo y") |]] or [[
  248. :call system("yes") |]]))
  249. feed('foo<c-c>')
  250. screen:expect([[
  251. ^ |
  252. {1:~ }|*12
  253. {5:-- INSERT --} |
  254. ]])
  255. end)
  256. end)
  257. describe('passing no input', function()
  258. it('returns the program output', function()
  259. if is_os('win') then
  260. eq('echoed\n', eval('system("echo echoed")'))
  261. else
  262. eq('echoed', eval('system("printf echoed")'))
  263. end
  264. end)
  265. it('to backgrounded command does not crash', function()
  266. -- This is indeterminate, just exercise the codepath. May get E5677.
  267. feed_command(
  268. 'call system(has("win32") ? "start /b /wait cmd /c echo echoed" : "printf echoed &")'
  269. )
  270. local v_errnum = string.match(eval('v:errmsg'), '^E%d*:')
  271. if v_errnum then
  272. eq('E5677:', v_errnum)
  273. end
  274. assert_alive()
  275. end)
  276. end)
  277. describe('passing input', function()
  278. it('returns the program output', function()
  279. eq('input', eval('system("cat -", "input")'))
  280. end)
  281. it('to backgrounded command does not crash', function()
  282. -- This is indeterminate, just exercise the codepath. May get E5677.
  283. feed_command('call system(has("win32") ? "start /b /wait more" : "cat - &", "input")')
  284. local v_errnum = string.match(eval('v:errmsg'), '^E%d*:')
  285. if v_errnum then
  286. eq('E5677:', v_errnum)
  287. end
  288. assert_alive()
  289. end)
  290. it('works with an empty string', function()
  291. eq('test\n', eval('system("echo test", "")'))
  292. assert_alive()
  293. end)
  294. end)
  295. describe('passing a lot of input', function()
  296. it('returns the program output', function()
  297. local input = {}
  298. -- write more than 1mb of data, which should be enough to overcome
  299. -- the os buffer limit and force multiple event loop iterations to write
  300. -- everything
  301. for _ = 1, 0xffff do
  302. input[#input + 1] = '01234567890ABCDEFabcdef'
  303. end
  304. input = table.concat(input, '\n')
  305. api.nvim_set_var('input', input)
  306. eq(input, eval('system("cat -", g:input)'))
  307. end)
  308. end)
  309. describe('Number input', function()
  310. it('is treated as a buffer id', function()
  311. command("put ='text in buffer 1'")
  312. eq('\ntext in buffer 1\n', eval('system("cat", 1)'))
  313. eq('Vim(echo):E86: Buffer 42 does not exist', exc_exec('echo system("cat", 42)'))
  314. end)
  315. end)
  316. describe('with output containing NULs', function()
  317. local fname = 'Xtest_functional_vimscript_system_nuls'
  318. before_each(create_file_with_nuls(fname))
  319. after_each(delete_file(fname))
  320. it('replaces NULs by SOH characters', function()
  321. eq('part1\001part2\001part3\n', eval([[system('"cat" "]] .. fname .. [["')]]))
  322. end)
  323. end)
  324. describe('input passed as List', function()
  325. it('joins List items with linefeed characters', function()
  326. eq('line1\nline2\nline3', eval("system('cat -', ['line1', 'line2', 'line3'])"))
  327. end)
  328. -- Notice that NULs are converted to SOH when the data is read back. This
  329. -- is inconsistent and is a good reason for the existence of the
  330. -- `systemlist()` function, where input and output map to the same
  331. -- characters(see the following tests with `systemlist()` below)
  332. describe('with linefeed characters inside List items', function()
  333. it('converts linefeed characters to NULs', function()
  334. eq(
  335. 'l1\001p2\nline2\001a\001b\nl3',
  336. eval([[system('cat -', ["l1\np2", "line2\na\nb", 'l3'])]])
  337. )
  338. end)
  339. end)
  340. describe('with leading/trailing whitespace characters on items', function()
  341. it('preserves whitespace, replacing linefeeds by NULs', function()
  342. eq(
  343. 'line \nline2\001\n\001line3',
  344. eval([[system('cat -', ['line ', "line2\n", "\nline3"])]])
  345. )
  346. end)
  347. end)
  348. end)
  349. it("with a program that doesn't close stdout will exit properly after passing input", function()
  350. local out = eval(string.format("system('%s', 'clip-data')", testprg('streams-test')))
  351. assert(out:sub(0, 5) == 'pid: ', out)
  352. os_kill(out:match('%d+'))
  353. end)
  354. end)
  355. describe('systemlist()', function()
  356. -- Similar to `system()`, but returns List instead of String.
  357. before_each(clear)
  358. it('sets v:shell_error', function()
  359. if is_os('win') then
  360. eval([[systemlist("cmd.exe /c exit")]])
  361. eq(0, eval('v:shell_error'))
  362. eval([[systemlist("cmd.exe /c exit 1")]])
  363. eq(1, eval('v:shell_error'))
  364. eval([[systemlist("cmd.exe /c exit 5")]])
  365. eq(5, eval('v:shell_error'))
  366. eval([[systemlist('this-should-not-exist')]])
  367. eq(1, eval('v:shell_error'))
  368. else
  369. eval([[systemlist("sh -c 'exit'")]])
  370. eq(0, eval('v:shell_error'))
  371. eval([[systemlist("sh -c 'exit 1'")]])
  372. eq(1, eval('v:shell_error'))
  373. eval([[systemlist("sh -c 'exit 5'")]])
  374. eq(5, eval('v:shell_error'))
  375. eval([[systemlist('this-should-not-exist')]])
  376. eq(127, eval('v:shell_error'))
  377. end
  378. end)
  379. describe('executes shell function', function()
  380. local screen
  381. before_each(function()
  382. screen = Screen.new()
  383. screen:attach()
  384. end)
  385. after_each(function()
  386. screen:detach()
  387. end)
  388. it('`echo` and waits for its return', function()
  389. feed(':call systemlist("echo")<cr>')
  390. screen:expect([[
  391. ^ |
  392. {1:~ }|*12
  393. :call systemlist("echo") |
  394. ]])
  395. end)
  396. it('`yes` interrupted with CTRL-C', function()
  397. feed(':call systemlist("yes | xargs")<cr>')
  398. screen:expect([[
  399. |
  400. {1:~ }|*12
  401. :call systemlist("yes | xargs") |
  402. ]])
  403. feed('<c-c>')
  404. screen:expect([[
  405. ^ |
  406. {1:~ }|*12
  407. Type :qa and press <Enter> to exit Nvim |
  408. ]])
  409. end)
  410. end)
  411. describe('passing string with linefeed characters as input', function()
  412. it('splits the output on linefeed characters', function()
  413. eq({ 'abc', 'def', 'ghi' }, eval([[systemlist("cat -", "abc\ndef\nghi")]]))
  414. end)
  415. end)
  416. describe('passing a lot of input', function()
  417. it('returns the program output', function()
  418. local input = {}
  419. for _ = 1, 0xffff do
  420. input[#input + 1] = '01234567890ABCDEFabcdef'
  421. end
  422. api.nvim_set_var('input', input)
  423. eq(input, eval('systemlist("cat -", g:input)'))
  424. end)
  425. end)
  426. describe('with output containing NULs', function()
  427. local fname = 'Xtest_functional_vimscript_systemlist_nuls'
  428. before_each(function()
  429. command('set ff=unix')
  430. create_file_with_nuls(fname)()
  431. end)
  432. after_each(delete_file(fname))
  433. it('replaces NULs by newline characters', function()
  434. eq({ 'part1\npart2\npart3' }, eval([[systemlist('"cat" "]] .. fname .. [["')]]))
  435. end)
  436. end)
  437. describe('input passed as List', function()
  438. it('joins list items with linefeed characters', function()
  439. eq({ 'line1', 'line2', 'line3' }, eval("systemlist('cat -', ['line1', 'line2', 'line3'])"))
  440. end)
  441. -- Unlike `system()` which uses SOH to represent NULs, with `systemlist()`
  442. -- input and output are the same.
  443. describe('with linefeed characters inside list items', function()
  444. it('converts linefeed characters to NULs', function()
  445. eq(
  446. { 'l1\np2', 'line2\na\nb', 'l3' },
  447. eval([[systemlist('cat -', ["l1\np2", "line2\na\nb", 'l3'])]])
  448. )
  449. end)
  450. end)
  451. describe('with leading/trailing whitespace characters on items', function()
  452. it('preserves whitespace, replacing linefeeds by NULs', function()
  453. eq(
  454. { 'line ', 'line2\n', '\nline3' },
  455. eval([[systemlist('cat -', ['line ', "line2\n", "\nline3"])]])
  456. )
  457. end)
  458. end)
  459. end)
  460. describe('handles empty lines', function()
  461. it('in the middle', function()
  462. eq({ 'line one', '', 'line two' }, eval("systemlist('cat',['line one','','line two'])"))
  463. end)
  464. it('in the beginning', function()
  465. eq({ '', 'line one', 'line two' }, eval("systemlist('cat',['','line one','line two'])"))
  466. end)
  467. end)
  468. describe('when keepempty option is', function()
  469. it('0, ignores trailing newline', function()
  470. eq({ 'aa', 'bb' }, eval("systemlist('cat',['aa','bb'],0)"))
  471. eq({ 'aa', 'bb' }, eval("systemlist('cat',['aa','bb',''],0)"))
  472. end)
  473. it('1, preserves trailing newline', function()
  474. eq({ 'aa', 'bb' }, eval("systemlist('cat',['aa','bb'],1)"))
  475. eq({ 'aa', 'bb', '' }, eval("systemlist('cat',['aa','bb',''],2)"))
  476. end)
  477. end)
  478. it("with a program that doesn't close stdout will exit properly after passing input", function()
  479. local out = eval(string.format("systemlist('%s', 'clip-data')", testprg('streams-test')))
  480. assert(out[1]:sub(0, 5) == 'pid: ', out)
  481. os_kill(out[1]:match('%d+'))
  482. end)
  483. it('powershell w/ UTF-8 text #13713', function()
  484. if not n.has_powershell() then
  485. pending('powershell not found', function() end)
  486. return
  487. end
  488. n.set_shell_powershell()
  489. eq({ is_os('win') and 'あ\r' or 'あ' }, eval([[systemlist('Write-Output あ')]]))
  490. -- Sanity test w/ default encoding
  491. -- * on Windows, expected to default to Western European enc
  492. -- * on Linux, expected to default to UTF8
  493. command([[let &shellcmdflag = '-NoLogo -NoProfile -ExecutionPolicy RemoteSigned -Command ']])
  494. eq({ is_os('win') and '?\r' or 'あ' }, eval([[systemlist('Write-Output あ')]]))
  495. end)
  496. end)
  497. describe('shell :!', function()
  498. before_each(clear)
  499. it(':{range}! with powershell filter/redirect #16271 #19250', function()
  500. local screen = Screen.new(500, 8)
  501. screen:attach()
  502. local found = n.set_shell_powershell(true)
  503. insert([[
  504. 3
  505. 1
  506. 4
  507. 2]])
  508. if is_os('win') then
  509. feed(':4verbose %!sort /R<cr>')
  510. screen:expect {
  511. any = [[Executing command: .?& { Get%-Content .* | & sort /R } 2>&1 | %%{ "$_" } | Out%-File .*; exit $LastExitCode"]],
  512. }
  513. else
  514. feed(':4verbose %!sort -r<cr>')
  515. screen:expect {
  516. any = [[Executing command: .?& { Get%-Content .* | & sort %-r } 2>&1 | %%{ "$_" } | Out%-File .*; exit $LastExitCode"]],
  517. }
  518. end
  519. feed('<CR>')
  520. if found then
  521. -- Not using fake powershell, so we can test the result.
  522. expect([[
  523. 4
  524. 3
  525. 2
  526. 1]])
  527. end
  528. end)
  529. it(':{range}! without redirecting to buffer', function()
  530. local screen = Screen.new(500, 10)
  531. screen:attach()
  532. insert([[
  533. 3
  534. 1
  535. 4
  536. 2]])
  537. feed(':4verbose %w !sort<cr>')
  538. if is_os('win') then
  539. screen:expect {
  540. any = [[Executing command: .?sort %< .*]],
  541. }
  542. else
  543. screen:expect {
  544. any = [[Executing command: .?%(sort%) %< .*]],
  545. }
  546. end
  547. feed('<CR>')
  548. n.set_shell_powershell(true)
  549. feed(':4verbose %w !sort<cr>')
  550. screen:expect {
  551. any = [[Executing command: .?& { Get%-Content .* | & sort }]],
  552. }
  553. feed('<CR>')
  554. n.expect_exit(command, 'qall!')
  555. end)
  556. end)