swapfile_preserve_recover_spec.lua 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. local Screen = require('test.functional.ui.screen')
  2. local helpers = require('test.functional.helpers')(after_each)
  3. local luv = require('luv')
  4. local eq, eval, expect, exec =
  5. helpers.eq, helpers.eval, helpers.expect, helpers.exec
  6. local assert_alive = helpers.assert_alive
  7. local clear = helpers.clear
  8. local command = helpers.command
  9. local feed = helpers.feed
  10. local funcs = helpers.funcs
  11. local nvim_prog = helpers.nvim_prog
  12. local ok = helpers.ok
  13. local rmdir = helpers.rmdir
  14. local new_argv = helpers.new_argv
  15. local new_pipename = helpers.new_pipename
  16. local pesc = helpers.pesc
  17. local os_kill = helpers.os_kill
  18. local set_session = helpers.set_session
  19. local spawn = helpers.spawn
  20. local nvim_async = helpers.nvim_async
  21. local expect_msg_seq = helpers.expect_msg_seq
  22. local pcall_err = helpers.pcall_err
  23. local mkdir = helpers.mkdir
  24. describe(':recover', function()
  25. before_each(clear)
  26. it('fails if given a non-existent swapfile', function()
  27. local swapname = 'bogus_swapfile'
  28. local swapname2 = 'bogus_swapfile.swp'
  29. eq('Vim(recover):E305: No swap file found for '..swapname,
  30. pcall_err(command, 'recover '..swapname)) -- Should not segfault. #2117
  31. -- Also check filename ending with ".swp". #9504
  32. eq('Vim(recover):E306: Cannot open '..swapname2,
  33. pcall_err(command, 'recover '..swapname2)) -- Should not segfault. #2117
  34. assert_alive()
  35. end)
  36. end)
  37. describe("preserve and (R)ecover with custom 'directory'", function()
  38. local swapdir = luv.cwd()..'/Xtest_recover_dir'
  39. local testfile = 'Xtest_recover_file1'
  40. -- Put swapdir at the start of the 'directory' list. #1836
  41. -- Note: `set swapfile` *must* go after `set directory`: otherwise it may
  42. -- attempt to create a swapfile in different directory.
  43. local init = [[
  44. set directory^=]]..swapdir:gsub([[\]], [[\\]])..[[//
  45. set swapfile fileformat=unix undolevels=-1
  46. ]]
  47. local nvim1
  48. before_each(function()
  49. nvim1 = spawn(new_argv())
  50. set_session(nvim1)
  51. rmdir(swapdir)
  52. mkdir(swapdir)
  53. end)
  54. after_each(function()
  55. command('%bwipeout!')
  56. rmdir(swapdir)
  57. end)
  58. local function setup_swapname()
  59. exec(init)
  60. command('edit! '..testfile)
  61. feed('isometext<esc>')
  62. exec('redir => g:swapname | silent swapname | redir END')
  63. return eval('g:swapname')
  64. end
  65. local function test_recover(swappath1)
  66. -- Start another Nvim instance.
  67. local nvim2 = spawn({nvim_prog, '-u', 'NONE', '-i', 'NONE', '--embed'},
  68. true)
  69. set_session(nvim2)
  70. exec(init)
  71. -- Use the "SwapExists" event to choose the (R)ecover choice at the dialog.
  72. command('autocmd SwapExists * let v:swapchoice = "r"')
  73. command('silent edit! '..testfile)
  74. exec('redir => g:swapname | silent swapname | redir END')
  75. local swappath2 = eval('g:swapname')
  76. expect('sometext')
  77. -- swapfile from session 1 should end in .swp
  78. eq(testfile..'.swp', string.match(swappath1, '[^%%]+$'))
  79. -- swapfile from session 2 should end in .swo
  80. eq(testfile..'.swo', string.match(swappath2, '[^%%]+$'))
  81. -- Verify that :swapname was not truncated (:help 'shortmess').
  82. ok(nil == string.find(swappath1, '%.%.%.'))
  83. ok(nil == string.find(swappath2, '%.%.%.'))
  84. end
  85. it('with :preserve and SIGKILL', function()
  86. local swappath1 = setup_swapname()
  87. command('preserve')
  88. os_kill(eval('getpid()'))
  89. test_recover(swappath1)
  90. end)
  91. it('closing stdio channel without :preserve #22096', function()
  92. local swappath1 = setup_swapname()
  93. nvim1:close()
  94. test_recover(swappath1)
  95. end)
  96. it('killing TUI process without :preserve #22096', function()
  97. helpers.skip(helpers.is_os('win'))
  98. local screen = Screen.new()
  99. screen:attach()
  100. local child_server = new_pipename()
  101. funcs.termopen({nvim_prog, '-u', 'NONE', '-i', 'NONE', '--listen', child_server})
  102. screen:expect({any = pesc('[No Name]')}) -- Wait for the child process to start.
  103. local child_session = helpers.connect(child_server)
  104. set_session(child_session)
  105. local swappath1 = setup_swapname()
  106. set_session(nvim1)
  107. command('call chanclose(&channel)') -- Kill the child process.
  108. screen:expect({any = pesc('[Process exited 1]')}) -- Wait for the child process to stop.
  109. test_recover(swappath1)
  110. end)
  111. end)
  112. describe('swapfile detection', function()
  113. local swapdir = luv.cwd()..'/Xtest_swapdialog_dir'
  114. local nvim0
  115. -- Put swapdir at the start of the 'directory' list. #1836
  116. -- Note: `set swapfile` *must* go after `set directory`: otherwise it may
  117. -- attempt to create a swapfile in different directory.
  118. local init = [[
  119. set directory^=]]..swapdir:gsub([[\]], [[\\]])..[[//
  120. set swapfile fileformat=unix nomodified undolevels=-1 nohidden
  121. ]]
  122. before_each(function()
  123. nvim0 = spawn(new_argv())
  124. set_session(nvim0)
  125. rmdir(swapdir)
  126. mkdir(swapdir)
  127. end)
  128. after_each(function()
  129. set_session(nvim0)
  130. command('%bwipeout!')
  131. rmdir(swapdir)
  132. end)
  133. it('always show swapfile dialog #8840 #9027', function()
  134. local testfile = 'Xtest_swapdialog_file1'
  135. local expected_no_dialog = '^'..(' '):rep(256)..'|\n'
  136. for _=1,37 do
  137. expected_no_dialog = expected_no_dialog..'~'..(' '):rep(255)..'|\n'
  138. end
  139. expected_no_dialog = expected_no_dialog..testfile..(' '):rep(216)..'0,0-1 All|\n'
  140. expected_no_dialog = expected_no_dialog..(' '):rep(256)..'|\n'
  141. exec(init)
  142. command('edit! '..testfile)
  143. feed('isometext<esc>')
  144. command('preserve')
  145. -- Start another Nvim instance.
  146. local nvim2 = spawn({nvim_prog, '-u', 'NONE', '-i', 'NONE', '--embed'}, true, nil, true)
  147. set_session(nvim2)
  148. local screen2 = Screen.new(256, 40)
  149. screen2:attach()
  150. exec(init)
  151. -- With shortmess+=F
  152. command('set shortmess+=F')
  153. feed(':edit '..testfile..'<CR>')
  154. screen2:expect{any=[[E325: ATTENTION.*]]..'\n'..[[Found a swap file by the name ".*]]
  155. ..[[Xtest_swapdialog_dir[/\].*]]..testfile..[[%.swp"]]}
  156. feed('e') -- Chose "Edit" at the swap dialog.
  157. screen2:expect(expected_no_dialog)
  158. -- With :silent and shortmess+=F
  159. feed(':silent edit %<CR>')
  160. screen2:expect{any=[[Found a swap file by the name ".*]]
  161. ..[[Xtest_swapdialog_dir[/\].*]]..testfile..[[%.swp"]]}
  162. feed('e') -- Chose "Edit" at the swap dialog.
  163. screen2:expect(expected_no_dialog)
  164. -- With :silent! and shortmess+=F
  165. feed(':silent! edit %<CR>')
  166. screen2:expect{any=[[Found a swap file by the name ".*]]
  167. ..[[Xtest_swapdialog_dir[/\].*]]..testfile..[[%.swp"]]}
  168. feed('e') -- Chose "Edit" at the swap dialog.
  169. screen2:expect(expected_no_dialog)
  170. -- With API (via eval/VimL) call and shortmess+=F
  171. feed(':call nvim_command("edit %")<CR>')
  172. screen2:expect{any=[[Found a swap file by the name ".*]]
  173. ..[[Xtest_swapdialog_dir[/\].*]]..testfile..[[%.swp"]]}
  174. feed('e') -- Chose "Edit" at the swap dialog.
  175. feed('<c-c>')
  176. screen2:expect(expected_no_dialog)
  177. -- With API call and shortmess+=F
  178. nvim_async('command', 'edit %')
  179. screen2:expect{any=[[Found a swap file by the name ".*]]
  180. ..[[Xtest_swapdialog_dir[/\].*]]..testfile..[[%.swp"]]}
  181. feed('e') -- Chose "Edit" at the swap dialog.
  182. expect_msg_seq({
  183. ignore={'redraw'},
  184. seqs={
  185. { {'notification', 'nvim_error_event', {0, 'Vim(edit):E325: ATTENTION'}},
  186. }
  187. }
  188. })
  189. feed('<cr>')
  190. nvim2:close()
  191. end)
  192. -- oldtest: Test_swap_prompt_splitwin()
  193. it('selecting "q" in the attention prompt', function()
  194. exec(init)
  195. command('edit Xfile1')
  196. command('preserve') -- should help to make sure the swap file exists
  197. local screen = Screen.new(75, 18)
  198. screen:set_default_attr_ids({
  199. [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
  200. [1] = {bold = true, foreground = Screen.colors.SeaGreen}, -- MoreMsg
  201. })
  202. local nvim1 = spawn(new_argv(), true, nil, true)
  203. set_session(nvim1)
  204. screen:attach()
  205. exec(init)
  206. feed(':split Xfile1\n')
  207. screen:expect({
  208. any = pesc('{1:[O]pen Read-Only, (E)dit anyway, (R)ecover, (Q)uit, (A)bort: }^')
  209. })
  210. feed('q')
  211. feed(':<CR>')
  212. screen:expect([[
  213. ^ |
  214. {0:~ }|
  215. {0:~ }|
  216. {0:~ }|
  217. {0:~ }|
  218. {0:~ }|
  219. {0:~ }|
  220. {0:~ }|
  221. {0:~ }|
  222. {0:~ }|
  223. {0:~ }|
  224. {0:~ }|
  225. {0:~ }|
  226. {0:~ }|
  227. {0:~ }|
  228. {0:~ }|
  229. {0:~ }|
  230. : |
  231. ]])
  232. nvim1:close()
  233. local nvim2 = spawn(new_argv(), true, nil, true)
  234. set_session(nvim2)
  235. screen:attach()
  236. exec(init)
  237. command('set more')
  238. command('au bufadd * let foo_w = wincol()')
  239. feed(':e Xfile1<CR>')
  240. screen:expect({any = pesc('{1:-- More --}^')})
  241. feed('<Space>')
  242. screen:expect({
  243. any = pesc('{1:[O]pen Read-Only, (E)dit anyway, (R)ecover, (Q)uit, (A)bort: }^')
  244. })
  245. feed('q')
  246. command([[echo 'hello']])
  247. screen:expect([[
  248. ^ |
  249. {0:~ }|
  250. {0:~ }|
  251. {0:~ }|
  252. {0:~ }|
  253. {0:~ }|
  254. {0:~ }|
  255. {0:~ }|
  256. {0:~ }|
  257. {0:~ }|
  258. {0:~ }|
  259. {0:~ }|
  260. {0:~ }|
  261. {0:~ }|
  262. {0:~ }|
  263. {0:~ }|
  264. {0:~ }|
  265. hello |
  266. ]])
  267. nvim2:close()
  268. end)
  269. -- oldtest: Test_nocatch_process_still_running()
  270. it('allows deleting swapfile created before boot vim-patch:8.2.2586', function()
  271. local screen = Screen.new(75, 30)
  272. screen:set_default_attr_ids({
  273. [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
  274. [1] = {bold = true, foreground = Screen.colors.SeaGreen}, -- MoreMsg
  275. [2] = {background = Screen.colors.Red, foreground = Screen.colors.White}, -- ErrorMsg
  276. })
  277. screen:attach()
  278. exec(init)
  279. command('set nohidden')
  280. exec([=[
  281. " Make a copy of the current swap file to "Xswap".
  282. " Return the name of the swap file.
  283. func CopySwapfile()
  284. preserve
  285. " get the name of the swap file
  286. let swname = split(execute("swapname"))[0]
  287. let swname = substitute(swname, '[[:blank:][:cntrl:]]*\(.\{-}\)[[:blank:][:cntrl:]]*$', '\1', '')
  288. " make a copy of the swap file in Xswap
  289. set binary
  290. exe 'sp ' . fnameescape(swname)
  291. w! Xswap
  292. set nobinary
  293. return swname
  294. endfunc
  295. ]=])
  296. -- Edit a file and grab its swapfile.
  297. exec([[
  298. edit Xswaptest
  299. call setline(1, ['a', 'b', 'c'])
  300. ]])
  301. local swname = funcs.CopySwapfile()
  302. -- Forget we edited this file
  303. exec([[
  304. new
  305. only!
  306. bwipe! Xswaptest
  307. ]])
  308. os.rename('Xswap', swname)
  309. feed(':edit Xswaptest<CR>')
  310. screen:expect({any = table.concat({
  311. pesc('{2:E325: ATTENTION}'),
  312. 'file name: .*Xswaptest',
  313. 'process ID: %d* %(STILL RUNNING%)',
  314. pesc('{1:[O]pen Read-Only, (E)dit anyway, (R)ecover, (Q)uit, (A)bort: }^'),
  315. }, '.*')})
  316. feed('e')
  317. -- Forget we edited this file
  318. exec([[
  319. new
  320. only!
  321. bwipe! Xswaptest
  322. ]])
  323. -- pretend that the swapfile was created before boot
  324. local atime = os.time() - luv.uptime() - 10
  325. luv.fs_utime(swname, atime, atime)
  326. feed(':edit Xswaptest<CR>')
  327. screen:expect({any = table.concat({
  328. pesc('{2:E325: ATTENTION}'),
  329. pesc('{1:[O]pen Read-Only, (E)dit anyway, (R)ecover, (D)elete it, (Q)uit, (A)bort: }^'),
  330. }, '.*')})
  331. feed('e')
  332. end)
  333. end)