xref: /vim-8.2.3635/src/testdir/test_recover.vim (revision 26190b27)
1" Test :recover
2
3source check.vim
4
5func Test_recover_root_dir()
6  " This used to access invalid memory.
7  split Xtest
8  set dir=/
9  call assert_fails('recover', 'E305:')
10  close!
11
12  if has('win32') || filewritable('/') == 2
13    " can write in / directory on MS-Windows
14    set dir=/notexist/
15  endif
16  call assert_fails('split Xtest', 'E303:')
17
18  " No error with empty 'directory' setting.
19  set directory=
20  split XtestOK
21  close!
22
23  set dir&
24endfunc
25
26" Make a copy of the current swap file to "Xswap".
27" Return the name of the swap file.
28func CopySwapfile()
29  preserve
30  " get the name of the swap file
31  let swname = split(execute("swapname"))[0]
32  let swname = substitute(swname, '[[:blank:][:cntrl:]]*\(.\{-}\)[[:blank:][:cntrl:]]*$', '\1', '')
33  " make a copy of the swap file in Xswap
34  set binary
35  exe 'sp ' . swname
36  w! Xswap
37  set nobinary
38  return swname
39endfunc
40
41" Inserts 10000 lines with text to fill the swap file with two levels of pointer
42" blocks.  Then recovers from the swap file and checks all text is restored.
43"
44" We need about 10000 lines of 100 characters to get two levels of pointer
45" blocks.
46func Test_swap_file()
47  set fileformat=unix undolevels=-1
48  edit! Xtest
49  let text = "\tabcdefghijklmnoparstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnoparstuvwxyz0123456789"
50  let i = 1
51  let linecount = 10000
52  while i <= linecount
53    call append(i - 1, i . text)
54    let i += 1
55  endwhile
56  $delete
57
58  let swname = CopySwapfile()
59
60  new
61  only!
62  bwipe! Xtest
63  call rename('Xswap', swname)
64  recover Xtest
65  call delete(swname)
66  let linedollar = line('$')
67  call assert_equal(linecount, linedollar)
68  if linedollar < linecount
69    let linecount = linedollar
70  endif
71  let i = 1
72  while i <= linecount
73    call assert_equal(i . text, getline(i))
74    let i += 1
75  endwhile
76
77  set undolevels&
78  enew! | only
79endfunc
80
81func Test_nocatch_process_still_running()
82  " sysinfo.uptime probably only works on Linux
83  if !has('linux')
84    let g:skipped_reason = 'only works on Linux'
85    return
86  endif
87  " the GUI dialog can't be handled
88  if has('gui_running')
89    let g:skipped_reason = 'only works in the terminal'
90    return
91  endif
92
93  " don't intercept existing swap file here
94  au! SwapExists
95
96  " Edit a file and grab its swapfile.
97  edit Xswaptest
98  call setline(1, ['a', 'b', 'c'])
99  let swname = CopySwapfile()
100
101  " Forget we edited this file
102  new
103  only!
104  bwipe! Xswaptest
105
106  call rename('Xswap', swname)
107  call feedkeys('e', 'tL')
108  redir => editOutput
109  edit Xswaptest
110  redir END
111  call assert_match('E325: ATTENTION', editOutput)
112  call assert_match('file name: .*Xswaptest', editOutput)
113  call assert_match('process ID: \d* (STILL RUNNING)', editOutput)
114
115  " Forget we edited this file
116  new
117  only!
118  bwipe! Xswaptest
119
120  " pretend we rebooted
121  call test_override("uptime", 0)
122  sleep 1
123
124  call rename('Xswap', swname)
125  call feedkeys('e', 'tL')
126  redir => editOutput
127  edit Xswaptest
128  redir END
129  call assert_match('E325: ATTENTION', editOutput)
130  call assert_notmatch('(STILL RUNNING)', editOutput)
131
132  call test_override("ALL", 0)
133  call delete(swname)
134endfunc
135
136" Test for :recover with multiple swap files
137func Test_recover_multiple_swap_files()
138  CheckUnix
139  new Xfile1
140  call setline(1, ['a', 'b', 'c'])
141  preserve
142  let b = readblob(swapname(''))
143  call writefile(b, '.Xfile1.swm')
144  call writefile(b, '.Xfile1.swn')
145  call writefile(b, '.Xfile1.swo')
146  %bw!
147  call feedkeys(":recover Xfile1\<CR>3\<CR>q", 'xt')
148  call assert_equal(['a', 'b', 'c'], getline(1, '$'))
149  " try using out-of-range number to select a swap file
150  bw!
151  call feedkeys(":recover Xfile1\<CR>4\<CR>q", 'xt')
152  call assert_equal('Xfile1', @%)
153  call assert_equal([''], getline(1, '$'))
154  bw!
155  call feedkeys(":recover Xfile1\<CR>0\<CR>q", 'xt')
156  call assert_equal('Xfile1', @%)
157  call assert_equal([''], getline(1, '$'))
158  bw!
159
160  call delete('.Xfile1.swm')
161  call delete('.Xfile1.swn')
162  call delete('.Xfile1.swo')
163endfunc
164
165" Test for :recover using an empty swap file
166func Test_recover_empty_swap_file()
167  CheckUnix
168  call writefile([], '.Xfile1.swp')
169  let msg = execute('recover Xfile1')
170  call assert_match('Unable to read block 0 from .Xfile1.swp', msg)
171  call assert_equal('Xfile1', @%)
172  bw!
173
174  " make sure there are no old swap files laying around
175  for f in glob('.sw?', 0, 1)
176    call delete(f)
177  endfor
178
179  " :recover from an empty buffer
180  call assert_fails('recover', 'E305:')
181  call delete('.Xfile1.swp')
182endfunc
183
184" Test for :recover using a corrupted swap file
185" Refer to the comments in the memline.c file for the swap file headers
186" definition.
187func Test_recover_corrupted_swap_file()
188  CheckUnix
189
190  " recover using a partial swap file
191  call writefile(0z1234, '.Xfile1.swp')
192  call assert_fails('recover Xfile1', 'E295:')
193  bw!
194
195  " recover using invalid content in the swap file
196  call writefile([repeat('1', 2*1024)], '.Xfile1.swp')
197  call assert_fails('recover Xfile1', 'E307:')
198  call delete('.Xfile1.swp')
199
200  " :recover using a swap file with a corrupted header
201  edit Xfile1
202  preserve
203  let sn = swapname('')
204  let b = readblob(sn)
205  let save_b = copy(b)
206  bw!
207
208  " Not all fields are written in a system-independent manner.  Detect whether
209  " the test is running on a little or big-endian system, so the correct
210  " corruption values can be set.
211  let little_endian = b[1008:1011] == 0z33323130
212  " The swap file header fields can be either 32-bit or 64-bit.
213  let system_64bit = b[1012:1015] == 0z00000000
214
215  " clear the B0_MAGIC_LONG field
216  if system_64bit
217    let b[1008:1015] = 0z00000000.00000000
218  else
219    let b[1008:1011] = 0z00000000
220  endif
221  call writefile(b, sn)
222  let msg = execute('recover Xfile1')
223  call assert_match('the file has been damaged', msg)
224  call assert_equal('Xfile1', @%)
225  call assert_equal([''], getline(1, '$'))
226  bw!
227
228  " reduce the page size
229  let b = copy(save_b)
230  let b[12:15] = 0z00010000
231  call writefile(b, sn)
232  let msg = execute('recover Xfile1')
233  call assert_match('page size is smaller than minimum value', msg)
234  call assert_equal('Xfile1', @%)
235  call assert_equal([''], getline(1, '$'))
236  bw!
237
238  " clear the pointer ID
239  let b = copy(save_b)
240  let b[4096:4097] = 0z0000
241  call writefile(b, sn)
242  call assert_fails('recover Xfile1', 'E310:')
243  call assert_equal('Xfile1', @%)
244  call assert_equal([''], getline(1, '$'))
245  bw!
246
247  " set the number of pointers in a pointer block to zero
248  let b = copy(save_b)
249  let b[4098:4099] = 0z0000
250  call writefile(b, sn)
251  call assert_fails('recover Xfile1', 'E312:')
252  call assert_equal('Xfile1', @%)
253  call assert_equal(['???EMPTY BLOCK'], getline(1, '$'))
254  bw!
255
256  " set the block number in a pointer entry to a negative number
257  let b = copy(save_b)
258  if system_64bit
259    let b[4104:4111] = little_endian ? 0z00000000.00000080 : 0z80000000.00000000
260  else
261    let b[4104:4107] = little_endian ? 0z00000080 : 0z80000000
262  endif
263  call writefile(b, sn)
264  call assert_fails('recover Xfile1', 'E312:')
265  call assert_equal('Xfile1', @%)
266  call assert_equal(['???LINES MISSING'], getline(1, '$'))
267  bw!
268
269  " clear the data block ID
270  let b = copy(save_b)
271  let b[8192:8193] = 0z0000
272  call writefile(b, sn)
273  call assert_fails('recover Xfile1', 'E312:')
274  call assert_equal('Xfile1', @%)
275  call assert_equal(['???BLOCK MISSING'], getline(1, '$'))
276  bw!
277
278  " set the number of lines in the data block to zero
279  let b = copy(save_b)
280  if system_64bit
281    let b[8208:8215] = 0z00000000.00000000
282  else
283    let b[8208:8211] = 0z00000000
284  endif
285  call writefile(b, sn)
286  call assert_fails('recover Xfile1', 'E312:')
287  call assert_equal('Xfile1', @%)
288  call assert_equal(['??? from here until ???END lines may have been inserted/deleted',
289        \ '???END'], getline(1, '$'))
290  bw!
291
292  " use an invalid text start for the lines in a data block
293  let b = copy(save_b)
294  if system_64bit
295    let b[8216:8219] = 0z00000000
296  else
297    let b[8212:8215] = 0z00000000
298  endif
299  call writefile(b, sn)
300  call assert_fails('recover Xfile1', 'E312:')
301  call assert_equal('Xfile1', @%)
302  call assert_equal(['???'], getline(1, '$'))
303  bw!
304
305  " use an incorrect text end (db_txt_end) for the data block
306  let b = copy(save_b)
307  let b[8204:8207] = little_endian ? 0z80000000 : 0z00000080
308  call writefile(b, sn)
309  call assert_fails('recover Xfile1', 'E312:')
310  call assert_equal('Xfile1', @%)
311  call assert_equal(['??? from here until ???END lines may be messed up', '',
312        \ '???END'], getline(1, '$'))
313  bw!
314
315  " remove the data block
316  let b = copy(save_b)
317  call writefile(b[:8191], sn)
318  call assert_fails('recover Xfile1', 'E312:')
319  call assert_equal('Xfile1', @%)
320  call assert_equal(['???MANY LINES MISSING'], getline(1, '$'))
321
322  bw!
323  call delete(sn)
324endfunc
325
326" Test for :recover using an encrypted swap file
327func Test_recover_encrypted_swap_file()
328  CheckUnix
329
330  " Recover an encrypted file from the swap file without the original file
331  new Xfile1
332  call feedkeys(":X\<CR>vim\<CR>vim\<CR>", 'xt')
333  call setline(1, ['aaa', 'bbb', 'ccc'])
334  preserve
335  let b = readblob('.Xfile1.swp')
336  call writefile(b, '.Xfile1.swm')
337  bw!
338  call feedkeys(":recover Xfile1\<CR>vim\<CR>\<CR>", 'xt')
339  call assert_equal(['aaa', 'bbb', 'ccc'], getline(1, '$'))
340  bw!
341  call delete('.Xfile1.swm')
342
343  " Recover an encrypted file from the swap file with the original file
344  new Xfile1
345  call feedkeys(":X\<CR>vim\<CR>vim\<CR>", 'xt')
346  call setline(1, ['aaa', 'bbb', 'ccc'])
347  update
348  call setline(1, ['111', '222', '333'])
349  preserve
350  let b = readblob('.Xfile1.swp')
351  call writefile(b, '.Xfile1.swm')
352  bw!
353  call feedkeys(":recover Xfile1\<CR>vim\<CR>\<CR>", 'xt')
354  call assert_equal(['111', '222', '333'], getline(1, '$'))
355  call assert_true(&modified)
356  bw!
357  call delete('.Xfile1.swm')
358  call delete('Xfile1')
359endfunc
360
361" Test for :recover using a unreadable swap file
362func Test_recover_unreadble_swap_file()
363  CheckUnix
364  CheckNotRoot
365  new Xfile1
366  let b = readblob('.Xfile1.swp')
367  call writefile(b, '.Xfile1.swm')
368  bw!
369  call setfperm('.Xfile1.swm', '-w-------')
370  call assert_fails('recover Xfile1', 'E306:')
371  call delete('.Xfile1.swm')
372endfunc
373
374" Test for using :recover when the original file and the swap file have the
375" same contents.
376func Test_recover_unmodified_file()
377  CheckUnix
378  call writefile(['aaa', 'bbb', 'ccc'], 'Xfile1')
379  edit Xfile1
380  preserve
381  let b = readblob('.Xfile1.swp')
382  %bw!
383  call writefile(b, '.Xfile1.swz')
384  let msg = execute('recover Xfile1')
385  call assert_equal(['aaa', 'bbb', 'ccc'], getline(1, '$'))
386  call assert_false(&modified)
387  call assert_match('Buffer contents equals file contents', msg)
388  bw!
389  call delete('Xfile1')
390  call delete('.Xfile1.swz')
391endfunc
392
393" Test for recovering a file when editing a symbolically linked file
394func Test_recover_symbolic_link()
395  CheckUnix
396  call writefile(['aaa', 'bbb', 'ccc'], 'Xfile1')
397  silent !ln -s Xfile1 Xfile2
398  edit Xfile2
399  call assert_equal('.Xfile1.swp', fnamemodify(swapname(''), ':t'))
400  preserve
401  let b = readblob('.Xfile1.swp')
402  %bw!
403  call writefile([], 'Xfile1')
404  call writefile(b, '.Xfile1.swp')
405  silent! recover Xfile2
406  call assert_equal(['aaa', 'bbb', 'ccc'], getline(1, '$'))
407  call assert_true(&modified)
408  update
409  %bw!
410  call assert_equal(['aaa', 'bbb', 'ccc'], readfile('Xfile1'))
411  call delete('Xfile1')
412  call delete('Xfile2')
413  call delete('.Xfile1.swp')
414endfunc
415
416" Test for recovering a file when an autocmd moves the cursor to an invalid
417" line. This used to result in an internal error (E315) which is fixed
418" by 8.2.2966.
419func Test_recover_invalid_cursor_pos()
420  call writefile([], 'Xfile1')
421  edit Xfile1
422  preserve
423  let b = readblob('.Xfile1.swp')
424  bw!
425  augroup Test
426    au!
427    au BufReadPost Xfile1 normal! 3G
428  augroup END
429  call writefile(range(1, 3), 'Xfile1')
430  call writefile(b, '.Xfile1.swp')
431  try
432    recover Xfile1
433  catch /E308:/
434    " this test is for the :E315 internal error.
435    " ignore the 'E308: Original file may have been changed' error
436  endtry
437  redraw!
438  augroup Test
439    au!
440  augroup END
441  augroup! Test
442  call delete('Xfile1')
443  call delete('.Xfile1.swp')
444endfunc
445
446" Test for recovering a buffer without a name
447func Test_noname_buffer()
448  new
449  call setline(1, ['one', 'two'])
450  preserve
451  let sn = swapname('')
452  let b = readblob(sn)
453  bw!
454  call writefile(b, sn)
455  exe "recover " .. sn
456  call assert_equal(['one', 'two'], getline(1, '$'))
457  call delete(sn)
458endfunc
459
460" vim: shiftwidth=2 sts=2 expandtab
461