neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

test_swap.vim (16283B)


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