xref: /vim-8.2.3635/src/testdir/test_swap.vim (revision 8b0e62c9)
1" Tests for the swap feature
2
3source check.vim
4source shared.vim
5source term_util.vim
6
7func s:swapname()
8  return trim(execute('swapname'))
9endfunc
10
11" Tests for 'directory' option.
12func Test_swap_directory()
13  CheckUnix
14
15  let content = ['start of testfile',
16	      \ 'line 2 Abcdefghij',
17	      \ 'line 3 Abcdefghij',
18	      \ 'end of testfile']
19  call writefile(content, 'Xtest1')
20
21  "  '.', swap file in the same directory as file
22  set dir=.,~
23
24  " Verify that the swap file doesn't exist in the current directory
25  call assert_equal([], glob(".Xtest1*.swp", 1, 1, 1))
26  edit Xtest1
27  let swfname = s:swapname()
28  call assert_equal([swfname], glob(swfname, 1, 1, 1))
29
30  " './dir', swap file in a directory relative to the file
31  set dir=./Xtest2,.,~
32
33  call mkdir("Xtest2")
34  edit Xtest1
35  call assert_equal([], glob(swfname, 1, 1, 1))
36  let swfname = "Xtest2/Xtest1.swp"
37  call assert_equal(swfname, s:swapname())
38  call assert_equal([swfname], glob("Xtest2/*", 1, 1, 1))
39
40  " 'dir', swap file in directory relative to the current dir
41  set dir=Xtest.je,~
42
43  call mkdir("Xtest.je")
44  call writefile(content, 'Xtest2/Xtest3')
45  edit Xtest2/Xtest3
46  call assert_equal(["Xtest2/Xtest3"], glob("Xtest2/*", 1, 1, 1))
47  let swfname = "Xtest.je/Xtest3.swp"
48  call assert_equal(swfname, s:swapname())
49  call assert_equal([swfname], glob("Xtest.je/*", 1, 1, 1))
50
51  set dir&
52  call delete("Xtest1")
53  call delete("Xtest2", "rf")
54  call delete("Xtest.je", "rf")
55endfunc
56
57func Test_swap_group()
58  CheckUnix
59
60  let groups = split(system('groups'))
61  if len(groups) <= 1
62    throw 'Skipped: need at least two groups, got ' . string(groups)
63  endif
64
65  try
66    call delete('Xtest')
67    split Xtest
68    call setline(1, 'just some text')
69    wq
70    if system('ls -l Xtest') !~ ' ' . groups[0] . ' \d'
71      throw 'Skipped: test file does not have the first group'
72    else
73      silent !chmod 640 Xtest
74      call system('chgrp ' . groups[1] . ' Xtest')
75      if system('ls -l Xtest') !~ ' ' . groups[1] . ' \d'
76	throw 'Skipped: cannot set second group on test file'
77      else
78	split Xtest
79	let swapname = s:swapname()
80	call assert_match('Xtest', swapname)
81	" Group of swapfile must now match original file.
82	call assert_match(' ' . groups[1] . ' \d', system('ls -l ' . swapname))
83
84	bwipe!
85      endif
86    endif
87  finally
88    call delete('Xtest')
89  endtry
90endfunc
91
92func Test_missing_dir()
93  call mkdir('Xswapdir')
94  exe 'set directory=' . getcwd() . '/Xswapdir'
95
96  call assert_equal('', glob('foo'))
97  call assert_equal('', glob('bar'))
98  edit foo/x.txt
99  " This should not give a warning for an existing swap file.
100  split bar/x.txt
101  only
102
103  " Delete the buffer so that swap file is removed before we try to delete the
104  " directory.  That fails on MS-Windows.
105  %bdelete!
106  set directory&
107  call delete('Xswapdir', 'rf')
108endfunc
109
110func Test_swapinfo()
111  new Xswapinfo
112  call setline(1, ['one', 'two', 'three'])
113  w
114  let fname = s:swapname()
115  call assert_match('Xswapinfo', fname)
116  let info = fname->swapinfo()
117
118  let ver = printf('VIM %d.%d', v:version / 100, v:version % 100)
119  call assert_equal(ver, info.version)
120
121  call assert_match('\w', info.user)
122  " host name is truncated to 39 bytes in the swap file
123  call assert_equal(hostname()[:38], info.host)
124  call assert_match('Xswapinfo', info.fname)
125  call assert_match(0, info.dirty)
126  call assert_equal(getpid(), info.pid)
127  call assert_match('^\d*$', info.mtime)
128  if has_key(info, 'inode')
129    call assert_match('\d', info.inode)
130  endif
131  bwipe!
132  call delete(fname)
133  call delete('Xswapinfo')
134
135  let info = swapinfo('doesnotexist')
136  call assert_equal('Cannot open file', info.error)
137
138  call writefile(['burp'], 'Xnotaswapfile')
139  let info = swapinfo('Xnotaswapfile')
140  call assert_equal('Cannot read file', info.error)
141  call delete('Xnotaswapfile')
142
143  call writefile([repeat('x', 10000)], 'Xnotaswapfile')
144  let info = swapinfo('Xnotaswapfile')
145  call assert_equal('Not a swap file', info.error)
146  call delete('Xnotaswapfile')
147endfunc
148
149func Test_swapname()
150  edit Xtest1
151  let expected = s:swapname()
152  call assert_equal(expected, swapname('%'))
153
154  new Xtest2
155  let buf = bufnr('%')
156  let expected = s:swapname()
157  wincmd p
158  call assert_equal(expected, buf->swapname())
159
160  new Xtest3
161  setlocal noswapfile
162  call assert_equal('', swapname('%'))
163
164  bwipe!
165  call delete('Xtest1')
166  call delete('Xtest2')
167  call delete('Xtest3')
168endfunc
169
170func Test_swapfile_delete()
171  autocmd! SwapExists
172  function s:swap_exists()
173    let v:swapchoice = s:swap_choice
174    let s:swapname = v:swapname
175    let s:filename = expand('<afile>')
176  endfunc
177  augroup test_swapfile_delete
178    autocmd!
179    autocmd SwapExists * call s:swap_exists()
180  augroup END
181
182
183  " Create a valid swapfile by editing a file.
184  split XswapfileText
185  call setline(1, ['one', 'two', 'three'])
186  write  " file is written, not modified
187  " read the swapfile as a Blob
188  let swapfile_name = swapname('%')
189  let swapfile_bytes = readfile(swapfile_name, 'B')
190
191  " Close the file and recreate the swap file.
192  " Now editing the file will run into the process still existing
193  quit
194  call writefile(swapfile_bytes, swapfile_name)
195  let s:swap_choice = 'e'
196  let s:swapname = ''
197  split XswapfileText
198  quit
199  call assert_equal(fnamemodify(swapfile_name, ':t'), fnamemodify(s:swapname, ':t'))
200
201  " This test won't work as root because root can successfully run kill(1, 0)
202  if !IsRoot()
203    " Write the swapfile with a modified PID, now it will be automatically
204    " deleted. Process 0x3fffffff most likely does not exist.
205    let swapfile_bytes[24:27] = 0zffffff3f
206    call writefile(swapfile_bytes, swapfile_name)
207    let s:swapname = ''
208    split XswapfileText
209    quit
210    call assert_equal('', s:swapname)
211  endif
212
213  " Now set the modified flag, the swap file will not be deleted
214  let swapfile_bytes[28 + 80 + 899] = 0x55
215  call writefile(swapfile_bytes, swapfile_name)
216  let s:swapname = ''
217  split XswapfileText
218  quit
219  call assert_equal(fnamemodify(swapfile_name, ':t'), fnamemodify(s:swapname, ':t'))
220
221  call delete('XswapfileText')
222  call delete(swapfile_name)
223  augroup test_swapfile_delete
224    autocmd!
225  augroup END
226  augroup! test_swapfile_delete
227endfunc
228
229func Test_swap_recover()
230  autocmd! SwapExists
231  augroup test_swap_recover
232    autocmd!
233    autocmd SwapExists * let v:swapchoice = 'r'
234  augroup END
235
236
237  call mkdir('Xswap')
238  let $Xswap = 'foo'  " Check for issue #4369.
239  set dir=Xswap//
240  " Create a valid swapfile by editing a file.
241  split Xswap/text
242  call setline(1, ['one', 'two', 'three'])
243  write  " file is written, not modified
244  " read the swapfile as a Blob
245  let swapfile_name = swapname('%')
246  let swapfile_bytes = readfile(swapfile_name, 'B')
247
248  " Close the file and recreate the swap file.
249  quit
250  call writefile(swapfile_bytes, swapfile_name)
251  " Edit the file again. This triggers recovery.
252  try
253    split Xswap/text
254  catch
255    " E308 should be caught, not E305.
256    call assert_exception('E308:')  " Original file may have been changed
257  endtry
258  " The file should be recovered.
259  call assert_equal(['one', 'two', 'three'], getline(1, 3))
260  quit!
261
262  call delete('Xswap/text')
263  call delete(swapfile_name)
264  call delete('Xswap', 'd')
265  unlet $Xswap
266  set dir&
267  augroup test_swap_recover
268    autocmd!
269  augroup END
270  augroup! test_swap_recover
271endfunc
272
273func Test_swap_recover_ext()
274  autocmd! SwapExists
275  augroup test_swap_recover_ext
276    autocmd!
277    autocmd SwapExists * let v:swapchoice = 'r'
278  augroup END
279
280  " Create a valid swapfile by editing a file with a special extension.
281  split Xtest.scr
282  call setline(1, ['one', 'two', 'three'])
283  write  " file is written, not modified
284  write  " write again to make sure the swapfile is created
285  " read the swapfile as a Blob
286  let swapfile_name = swapname('%')
287  let swapfile_bytes = readfile(swapfile_name, 'B')
288
289  " Close and delete the file and recreate the swap file.
290  quit
291  call delete('Xtest.scr')
292  call writefile(swapfile_bytes, swapfile_name)
293  " Edit the file again. This triggers recovery.
294  try
295    split Xtest.scr
296  catch
297    " E308 should be caught, not E306.
298    call assert_exception('E308:')  " Original file may have been changed
299  endtry
300  " The file should be recovered.
301  call assert_equal(['one', 'two', 'three'], getline(1, 3))
302  quit!
303
304  call delete('Xtest.scr')
305  call delete(swapfile_name)
306  augroup test_swap_recover_ext
307    autocmd!
308  augroup END
309  augroup! test_swap_recover_ext
310endfunc
311
312" Test for closing a split window automatically when a swap file is detected
313" and 'Q' is selected in the confirmation prompt.
314func Test_swap_split_win()
315  autocmd! SwapExists
316  augroup test_swap_splitwin
317    autocmd!
318    autocmd SwapExists * let v:swapchoice = 'q'
319  augroup END
320
321  " Create a valid swapfile by editing a file with a special extension.
322  split Xtest.scr
323  call setline(1, ['one', 'two', 'three'])
324  write  " file is written, not modified
325  write  " write again to make sure the swapfile is created
326  " read the swapfile as a Blob
327  let swapfile_name = swapname('%')
328  let swapfile_bytes = readfile(swapfile_name, 'B')
329
330  " Close and delete the file and recreate the swap file.
331  quit
332  call delete('Xtest.scr')
333  call writefile(swapfile_bytes, swapfile_name)
334  " Split edit the file again. This should fail to open the window
335  try
336    split Xtest.scr
337  catch
338    " E308 should be caught, not E306.
339    call assert_exception('E308:')  " Original file may have been changed
340  endtry
341  call assert_equal(1, winnr('$'))
342
343  call delete('Xtest.scr')
344  call delete(swapfile_name)
345
346  augroup test_swap_splitwin
347      autocmd!
348  augroup END
349  augroup! test_swap_splitwin
350endfunc
351
352" Test for selecting 'q' in the attention prompt
353func Test_swap_prompt_splitwin()
354  CheckRunVimInTerminal
355
356  call writefile(['foo bar'], 'Xfile1')
357  edit Xfile1
358  preserve  " should help to make sure the swap file exists
359
360  let buf = RunVimInTerminal('', {'rows': 20})
361  call term_sendkeys(buf, ":set nomore\n")
362  call term_sendkeys(buf, ":set noruler\n")
363
364  call term_sendkeys(buf, ":split Xfile1\n")
365  call TermWait(buf)
366  call WaitForAssert({-> assert_match('^\[O\]pen Read-Only, (E)dit anyway, (R)ecover, (Q)uit, (A)bort: $', term_getline(buf, 20))})
367  call term_sendkeys(buf, "q")
368  call TermWait(buf)
369  call term_sendkeys(buf, ":\<CR>")
370  call WaitForAssert({-> assert_match('^:$', term_getline(buf, 20))})
371  call term_sendkeys(buf, ":echomsg winnr('$')\<CR>")
372  call TermWait(buf)
373  call WaitForAssert({-> assert_match('^1$', term_getline(buf, 20))})
374  call StopVimInTerminal(buf)
375
376  " This caused Vim to crash when typing "q" at the swap file prompt.
377  let buf = RunVimInTerminal('-c "au bufadd * let foo_w = wincol()"', {'rows': 18})
378  call term_sendkeys(buf, ":e Xfile1\<CR>")
379  call WaitForAssert({-> assert_match('More', term_getline(buf, 18))})
380  call term_sendkeys(buf, " ")
381  call WaitForAssert({-> assert_match('^\[O\]pen Read-Only, (E)dit anyway, (R)ecover, (Q)uit, (A)bort:', term_getline(buf, 18))})
382  call term_sendkeys(buf, "q")
383  call TermWait(buf)
384  " check that Vim is still running
385  call term_sendkeys(buf, ":echo 'hello'\<CR>")
386  call WaitForAssert({-> assert_match('^hello', term_getline(buf, 18))})
387  call term_sendkeys(buf, ":%bwipe!\<CR>")
388  call StopVimInTerminal(buf)
389
390  %bwipe!
391  call delete('Xfile1')
392endfunc
393
394func Test_swap_symlink()
395  CheckUnix
396
397  call writefile(['text'], 'Xtestfile')
398  silent !ln -s -f Xtestfile Xtestlink
399
400  set dir=.
401
402  " Test that swap file uses the name of the file when editing through a
403  " symbolic link (so that editing the file twice is detected)
404  edit Xtestlink
405  call assert_match('Xtestfile\.swp$', s:swapname())
406  bwipe!
407
408  call mkdir('Xswapdir')
409  exe 'set dir=' . getcwd() . '/Xswapdir//'
410
411  " Check that this also works when 'directory' ends with '//'
412  edit Xtestlink
413  call assert_match('Xswapdir[/\\]%.*testdir%Xtestfile\.swp$', s:swapname())
414  bwipe!
415
416  set dir&
417  call delete('Xtestfile')
418  call delete('Xtestlink')
419  call delete('Xswapdir', 'rf')
420endfunc
421
422func s:get_unused_pid(base)
423  if has('job')
424    " Execute 'echo' as a temporary job, and return its pid as an unused pid.
425    if has('win32')
426      let cmd = 'cmd /c echo'
427    else
428      let cmd = 'echo'
429    endif
430    let j = job_start(cmd)
431    while job_status(j) ==# 'run'
432      sleep 10m
433    endwhile
434    if job_status(j) ==# 'dead'
435      return job_info(j).process
436    endif
437  endif
438  " Must add four for MS-Windows to see it as a different one.
439  return a:base + 4
440endfunc
441
442func s:blob_to_pid(b)
443  return a:b[3] * 16777216 + a:b[2] * 65536 + a:b[1] * 256 + a:b[0]
444endfunc
445
446func s:pid_to_blob(i)
447  let b = 0z
448  let b[0] = and(a:i, 0xff)
449  let b[1] = and(a:i / 256, 0xff)
450  let b[2] = and(a:i / 65536, 0xff)
451  let b[3] = and(a:i / 16777216, 0xff)
452  return b
453endfunc
454
455func Test_swap_auto_delete()
456  " Create a valid swapfile by editing a file with a special extension.
457  split Xtest.scr
458  call setline(1, ['one', 'two', 'three'])
459  write  " file is written, not modified
460  write  " write again to make sure the swapfile is created
461  " read the swapfile as a Blob
462  let swapfile_name = swapname('%')
463  let swapfile_bytes = readfile(swapfile_name, 'B')
464
465  " Forget about the file, recreate the swap file, then edit it again.  The
466  " swap file should be automatically deleted.
467  bwipe!
468  " Change the process ID to avoid the "still running" warning.
469  let swapfile_bytes[24:27] = s:pid_to_blob(s:get_unused_pid(
470        \ s:blob_to_pid(swapfile_bytes[24:27])))
471  call writefile(swapfile_bytes, swapfile_name)
472  edit Xtest.scr
473  " will end up using the same swap file after deleting the existing one
474  call assert_equal(swapfile_name, swapname('%'))
475  bwipe!
476
477  " create the swap file again, but change the host name so that it won't be
478  " deleted
479  autocmd! SwapExists
480  augroup test_swap_recover_ext
481    autocmd!
482    autocmd SwapExists * let v:swapchoice = 'e'
483  augroup END
484
485  " change the host name
486  let swapfile_bytes[28 + 40] = swapfile_bytes[28 + 40] + 2
487  call writefile(swapfile_bytes, swapfile_name)
488  edit Xtest.scr
489  call assert_equal(1, filereadable(swapfile_name))
490  " will use another same swap file name
491  call assert_notequal(swapfile_name, swapname('%'))
492  bwipe!
493
494  call delete('Xtest.scr')
495  call delete(swapfile_name)
496  augroup test_swap_recover_ext
497    autocmd!
498  augroup END
499  augroup! test_swap_recover_ext
500endfunc
501
502" Test for renaming a buffer when the swap file is deleted out-of-band
503func Test_missing_swap_file()
504  CheckUnix
505  new Xfile2
506  call delete(swapname(''))
507  call assert_fails('file Xfile3', 'E301:')
508  call assert_equal('Xfile3', bufname())
509  call assert_true(bufexists('Xfile2'))
510  call assert_true(bufexists('Xfile3'))
511  %bw!
512endfunc
513
514" Test for :preserve command
515func Test_preserve()
516  new Xfile4
517  setlocal noswapfile
518  call assert_fails('preserve', 'E313:')
519  bw!
520endfunc
521
522" Test for the v:swapchoice variable
523func Test_swapchoice()
524  call writefile(['aaa', 'bbb'], 'Xfile5')
525  edit Xfile5
526  preserve
527  let swapfname = swapname('')
528  let b = readblob(swapfname)
529  bw!
530  call writefile(b, swapfname)
531
532  autocmd! SwapExists
533
534  " Test for v:swapchoice = 'o' (readonly)
535  augroup test_swapchoice
536    autocmd!
537    autocmd SwapExists * let v:swapchoice = 'o'
538  augroup END
539  edit Xfile5
540  call assert_true(&readonly)
541  call assert_equal(['aaa', 'bbb'], getline(1, '$'))
542  %bw!
543  call assert_true(filereadable(swapfname))
544
545  " Test for v:swapchoice = 'a' (abort)
546  augroup test_swapchoice
547    autocmd!
548    autocmd SwapExists * let v:swapchoice = 'a'
549  augroup END
550  try
551    edit Xfile5
552  catch /^Vim:Interrupt$/
553  endtry
554  call assert_equal('', @%)
555  call assert_true(bufexists('Xfile5'))
556  %bw!
557  call assert_true(filereadable(swapfname))
558
559  " Test for v:swapchoice = 'd' (delete)
560  augroup test_swapchoice
561    autocmd!
562    autocmd SwapExists * let v:swapchoice = 'd'
563  augroup END
564  edit Xfile5
565  call assert_equal('Xfile5', @%)
566  %bw!
567  call assert_false(filereadable(swapfname))
568
569  call delete('Xfile5')
570  call delete(swapfname)
571  augroup test_swapchoice
572    autocmd!
573  augroup END
574  augroup! test_swapchoice
575endfunc
576
577func Test_no_swap_file()
578  call assert_equal("\nNo swap file", execute('swapname'))
579endfunc
580
581" vim: shiftwidth=2 sts=2 expandtab
582