xref: /vim-8.2.3635/src/testdir/test_recover.vim (revision 37f341d7)
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  " The B0_MAGIC_LONG field may be 32-bit or 64-bit, depending on the system,
212  " even though the value stored is only 32-bits.  Therefore, need to check
213  " both the high and low 32-bits to compute these values.
214  let little_endian = (b[1008:1011] == 0z33323130) || (b[1012:1015] == 0z33323130)
215  let system_64bit = little_endian ? (b[1012:1015] == 0z00000000) : (b[1008:1011] == 0z00000000)
216
217  " clear the B0_MAGIC_LONG field
218  if system_64bit
219    let b[1008:1015] = 0z00000000.00000000
220  else
221    let b[1008:1011] = 0z00000000
222  endif
223  call writefile(b, sn)
224  let msg = execute('recover Xfile1')
225  call assert_match('the file has been damaged', msg)
226  call assert_equal('Xfile1', @%)
227  call assert_equal([''], getline(1, '$'))
228  bw!
229
230  " reduce the page size
231  let b = copy(save_b)
232  let b[12:15] = 0z00010000
233  call writefile(b, sn)
234  let msg = execute('recover Xfile1')
235  call assert_match('page size is smaller than minimum value', msg)
236  call assert_equal('Xfile1', @%)
237  call assert_equal([''], getline(1, '$'))
238  bw!
239
240  " clear the pointer ID
241  let b = copy(save_b)
242  let b[4096:4097] = 0z0000
243  call writefile(b, sn)
244  call assert_fails('recover Xfile1', 'E310:')
245  call assert_equal('Xfile1', @%)
246  call assert_equal([''], getline(1, '$'))
247  bw!
248
249  " set the number of pointers in a pointer block to zero
250  let b = copy(save_b)
251  let b[4098:4099] = 0z0000
252  call writefile(b, sn)
253  call assert_fails('recover Xfile1', 'E312:')
254  call assert_equal('Xfile1', @%)
255  call assert_equal(['???EMPTY BLOCK'], getline(1, '$'))
256  bw!
257
258  " set the block number in a pointer entry to a negative number
259  let b = copy(save_b)
260  if system_64bit
261    let b[4104:4111] = little_endian ? 0z00000000.00000080 : 0z80000000.00000000
262  else
263    let b[4104:4107] = little_endian ? 0z00000080 : 0z80000000
264  endif
265  call writefile(b, sn)
266  call assert_fails('recover Xfile1', 'E312:')
267  call assert_equal('Xfile1', @%)
268  call assert_equal(['???LINES MISSING'], getline(1, '$'))
269  bw!
270
271  " clear the data block ID
272  let b = copy(save_b)
273  let b[8192:8193] = 0z0000
274  call writefile(b, sn)
275  call assert_fails('recover Xfile1', 'E312:')
276  call assert_equal('Xfile1', @%)
277  call assert_equal(['???BLOCK MISSING'], getline(1, '$'))
278  bw!
279
280  " set the number of lines in the data block to zero
281  let b = copy(save_b)
282  if system_64bit
283    let b[8208:8215] = 0z00000000.00000000
284  else
285    let b[8208:8211] = 0z00000000
286  endif
287  call writefile(b, sn)
288  call assert_fails('recover Xfile1', 'E312:')
289  call assert_equal('Xfile1', @%)
290  call assert_equal(['??? from here until ???END lines may have been inserted/deleted',
291        \ '???END'], getline(1, '$'))
292  bw!
293
294  " use an invalid text start for the lines in a data block
295  let b = copy(save_b)
296  if system_64bit
297    let b[8216:8219] = 0z00000000
298  else
299    let b[8212:8215] = 0z00000000
300  endif
301  call writefile(b, sn)
302  call assert_fails('recover Xfile1', 'E312:')
303  call assert_equal('Xfile1', @%)
304  call assert_equal(['???'], getline(1, '$'))
305  bw!
306
307  " use an incorrect text end (db_txt_end) for the data block
308  let b = copy(save_b)
309  let b[8204:8207] = little_endian ? 0z80000000 : 0z00000080
310  call writefile(b, sn)
311  call assert_fails('recover Xfile1', 'E312:')
312  call assert_equal('Xfile1', @%)
313  call assert_equal(['??? from here until ???END lines may be messed up', '',
314        \ '???END'], getline(1, '$'))
315  bw!
316
317  " remove the data block
318  let b = copy(save_b)
319  call writefile(b[:8191], sn)
320  call assert_fails('recover Xfile1', 'E312:')
321  call assert_equal('Xfile1', @%)
322  call assert_equal(['???MANY LINES MISSING'], getline(1, '$'))
323
324  bw!
325  call delete(sn)
326endfunc
327
328" Test for :recover using an encrypted swap file
329func Test_recover_encrypted_swap_file()
330  CheckUnix
331
332  " Recover an encrypted file from the swap file without the original file
333  new Xfile1
334  call feedkeys(":X\<CR>vim\<CR>vim\<CR>", 'xt')
335  call setline(1, ['aaa', 'bbb', 'ccc'])
336  preserve
337  let b = readblob('.Xfile1.swp')
338  call writefile(b, '.Xfile1.swm')
339  bw!
340  call feedkeys(":recover Xfile1\<CR>vim\<CR>\<CR>", 'xt')
341  call assert_equal(['aaa', 'bbb', 'ccc'], getline(1, '$'))
342  bw!
343  call delete('.Xfile1.swm')
344
345  " Recover an encrypted file from the swap file with the original file
346  new Xfile1
347  call feedkeys(":X\<CR>vim\<CR>vim\<CR>", 'xt')
348  call setline(1, ['aaa', 'bbb', 'ccc'])
349  update
350  call setline(1, ['111', '222', '333'])
351  preserve
352  let b = readblob('.Xfile1.swp')
353  call writefile(b, '.Xfile1.swm')
354  bw!
355  call feedkeys(":recover Xfile1\<CR>vim\<CR>\<CR>", 'xt')
356  call assert_equal(['111', '222', '333'], getline(1, '$'))
357  call assert_true(&modified)
358  bw!
359  call delete('.Xfile1.swm')
360  call delete('Xfile1')
361endfunc
362
363" Test for :recover using a unreadable swap file
364func Test_recover_unreadble_swap_file()
365  CheckUnix
366  CheckNotRoot
367  new Xfile1
368  let b = readblob('.Xfile1.swp')
369  call writefile(b, '.Xfile1.swm')
370  bw!
371  call setfperm('.Xfile1.swm', '-w-------')
372  call assert_fails('recover Xfile1', 'E306:')
373  call delete('.Xfile1.swm')
374endfunc
375
376" Test for using :recover when the original file and the swap file have the
377" same contents.
378func Test_recover_unmodified_file()
379  CheckUnix
380  call writefile(['aaa', 'bbb', 'ccc'], 'Xfile1')
381  edit Xfile1
382  preserve
383  let b = readblob('.Xfile1.swp')
384  %bw!
385  call writefile(b, '.Xfile1.swz')
386  let msg = execute('recover Xfile1')
387  call assert_equal(['aaa', 'bbb', 'ccc'], getline(1, '$'))
388  call assert_false(&modified)
389  call assert_match('Buffer contents equals file contents', msg)
390  bw!
391  call delete('Xfile1')
392  call delete('.Xfile1.swz')
393endfunc
394
395" Test for recovering a file when editing a symbolically linked file
396func Test_recover_symbolic_link()
397  CheckUnix
398  call writefile(['aaa', 'bbb', 'ccc'], 'Xfile1')
399  silent !ln -s Xfile1 Xfile2
400  edit Xfile2
401  call assert_equal('.Xfile1.swp', fnamemodify(swapname(''), ':t'))
402  preserve
403  let b = readblob('.Xfile1.swp')
404  %bw!
405  call writefile([], 'Xfile1')
406  call writefile(b, '.Xfile1.swp')
407  silent! recover Xfile2
408  call assert_equal(['aaa', 'bbb', 'ccc'], getline(1, '$'))
409  call assert_true(&modified)
410  update
411  %bw!
412  call assert_equal(['aaa', 'bbb', 'ccc'], readfile('Xfile1'))
413  call delete('Xfile1')
414  call delete('Xfile2')
415  call delete('.Xfile1.swp')
416endfunc
417
418" Test for recovering a file when an autocmd moves the cursor to an invalid
419" line. This used to result in an internal error (E315) which is fixed
420" by 8.2.2966.
421func Test_recover_invalid_cursor_pos()
422  call writefile([], 'Xfile1')
423  edit Xfile1
424  preserve
425  let b = readblob('.Xfile1.swp')
426  bw!
427  augroup Test
428    au!
429    au BufReadPost Xfile1 normal! 3G
430  augroup END
431  call writefile(range(1, 3), 'Xfile1')
432  call writefile(b, '.Xfile1.swp')
433  try
434    recover Xfile1
435  catch /E308:/
436    " this test is for the :E315 internal error.
437    " ignore the 'E308: Original file may have been changed' error
438  endtry
439  redraw!
440  augroup Test
441    au!
442  augroup END
443  augroup! Test
444  call delete('Xfile1')
445  call delete('.Xfile1.swp')
446endfunc
447
448" Test for recovering a buffer without a name
449func Test_noname_buffer()
450  new
451  call setline(1, ['one', 'two'])
452  preserve
453  let sn = swapname('')
454  let b = readblob(sn)
455  bw!
456  call writefile(b, sn)
457  exe "recover " .. sn
458  call assert_equal(['one', 'two'], getline(1, '$'))
459  call delete(sn)
460endfunc
461
462" vim: shiftwidth=2 sts=2 expandtab
463