blob: 13437cd8ed6d5a404d53037e1a901f83e24de1d4 [file] [log] [blame]
Bram Moolenaarc525e3a2017-02-18 16:59:02 +01001" Test :recover
2
Bram Moolenaarf52f0602021-03-10 21:26:37 +01003source check.vim
4
Bram Moolenaarc525e3a2017-02-18 16:59:02 +01005func 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
Bram Moolenaar2a0b06d2017-05-18 16:23:43 +020012 if has('win32') || filewritable('/') == 2
Bram Moolenaar80345202017-02-18 22:43:19 +010013 " can write in / directory on MS-Windows
14 set dir=/notexist/
15 endif
Bram Moolenaarc525e3a2017-02-18 16:59:02 +010016 call assert_fails('split Xtest', 'E303:')
Bram Moolenaar00e192b2019-10-19 17:01:28 +020017
18 " No error with empty 'directory' setting.
19 set directory=
20 split XtestOK
21 close!
22
Bram Moolenaarc525e3a2017-02-18 16:59:02 +010023 set dir&
24endfunc
25
Bram Moolenaarf52f0602021-03-10 21:26:37 +010026" 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
Bram Moolenaar4a6fcf82017-10-12 21:29:22 +020041" 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()
Bram Moolenaar67418d92017-10-15 22:07:39 +020047 set fileformat=unix undolevels=-1
Bram Moolenaar4a6fcf82017-10-12 21:29:22 +020048 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
Bram Moolenaarf52f0602021-03-10 21:26:37 +010057
58 let swname = CopySwapfile()
59
Bram Moolenaar4a6fcf82017-10-12 21:29:22 +020060 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
Bram Moolenaar6d91bcb2020-08-12 18:50:36 +020080
Bram Moolenaarf52f0602021-03-10 21:26:37 +010081func Test_nocatch_process_still_running()
Bram Moolenaar6635ae12021-03-10 21:46:39 +010082 " sysinfo.uptime probably only works on Linux
Bram Moolenaar776b9542021-03-10 22:27:48 +010083 if !has('linux')
84 let g:skipped_reason = 'only works on Linux'
85 return
86 endif
Bram Moolenaar6635ae12021-03-10 21:46:39 +010087 " the GUI dialog can't be handled
Bram Moolenaar776b9542021-03-10 22:27:48 +010088 if has('gui_running')
89 let g:skipped_reason = 'only works in the terminal'
90 return
91 endif
Bram Moolenaarf52f0602021-03-10 21:26:37 +010092
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
Bram Moolenaarf52f0602021-03-10 21:26:37 +0100124 call feedkeys('e', 'tL')
125 redir => editOutput
126 edit Xswaptest
127 redir END
128 call assert_match('E325: ATTENTION', editOutput)
129 call assert_notmatch('(STILL RUNNING)', editOutput)
130
131 call test_override("ALL", 0)
132 call delete(swname)
133endfunc
134
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200135" Test for :recover with multiple swap files
136func Test_recover_multiple_swap_files()
137 CheckUnix
138 new Xfile1
139 call setline(1, ['a', 'b', 'c'])
140 preserve
Yegappan Lakshmanan99285552021-06-06 17:12:46 +0200141 let b = readblob(swapname(''))
Bram Moolenaardb77cb32022-10-05 21:45:30 +0100142 call writefile(b, '.Xfile1.swm', 'D')
143 call writefile(b, '.Xfile1.swn', 'D')
144 call writefile(b, '.Xfile1.swo', 'D')
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200145 %bw!
146 call feedkeys(":recover Xfile1\<CR>3\<CR>q", 'xt')
147 call assert_equal(['a', 'b', 'c'], getline(1, '$'))
Yegappan Lakshmanan8cf02e52021-06-07 20:41:22 +0200148 " try using out-of-range number to select a swap file
149 bw!
150 call feedkeys(":recover Xfile1\<CR>4\<CR>q", 'xt')
151 call assert_equal('Xfile1', @%)
152 call assert_equal([''], getline(1, '$'))
153 bw!
154 call feedkeys(":recover Xfile1\<CR>0\<CR>q", 'xt')
155 call assert_equal('Xfile1', @%)
156 call assert_equal([''], getline(1, '$'))
157 bw!
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200158endfunc
159
160" Test for :recover using an empty swap file
161func Test_recover_empty_swap_file()
162 CheckUnix
Bram Moolenaardb77cb32022-10-05 21:45:30 +0100163 call writefile([], '.Xfile1.swp', 'D')
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200164 let msg = execute('recover Xfile1')
165 call assert_match('Unable to read block 0 from .Xfile1.swp', msg)
166 call assert_equal('Xfile1', @%)
167 bw!
Bram Moolenaarf2a8baf2021-09-14 22:58:23 +0200168
169 " make sure there are no old swap files laying around
170 for f in glob('.sw?', 0, 1)
171 call delete(f)
172 endfor
173
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200174 " :recover from an empty buffer
175 call assert_fails('recover', 'E305:')
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200176endfunc
177
178" Test for :recover using a corrupted swap file
Yegappan Lakshmanan8cf02e52021-06-07 20:41:22 +0200179" Refer to the comments in the memline.c file for the swap file headers
180" definition.
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200181func Test_recover_corrupted_swap_file()
182 CheckUnix
Yegappan Lakshmanan99285552021-06-06 17:12:46 +0200183
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200184 " recover using a partial swap file
185 call writefile(0z1234, '.Xfile1.swp')
186 call assert_fails('recover Xfile1', 'E295:')
187 bw!
188
189 " recover using invalid content in the swap file
190 call writefile([repeat('1', 2*1024)], '.Xfile1.swp')
191 call assert_fails('recover Xfile1', 'E307:')
192 call delete('.Xfile1.swp')
193
194 " :recover using a swap file with a corrupted header
195 edit Xfile1
196 preserve
197 let sn = swapname('')
198 let b = readblob(sn)
Yegappan Lakshmanan99285552021-06-06 17:12:46 +0200199 let save_b = copy(b)
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200200 bw!
Yegappan Lakshmanan99285552021-06-06 17:12:46 +0200201
James McCoy6654ca72021-06-12 14:05:41 +0200202 " Not all fields are written in a system-independent manner. Detect whether
203 " the test is running on a little or big-endian system, so the correct
204 " corruption values can be set.
James McCoy37f341d2021-10-11 21:04:37 +0100205 " The B0_MAGIC_LONG field may be 32-bit or 64-bit, depending on the system,
206 " even though the value stored is only 32-bits. Therefore, need to check
207 " both the high and low 32-bits to compute these values.
208 let little_endian = (b[1008:1011] == 0z33323130) || (b[1012:1015] == 0z33323130)
209 let system_64bit = little_endian ? (b[1012:1015] == 0z00000000) : (b[1008:1011] == 0z00000000)
Yegappan Lakshmanan8cf02e52021-06-07 20:41:22 +0200210
James McCoy6654ca72021-06-12 14:05:41 +0200211 " clear the B0_MAGIC_LONG field
Yegappan Lakshmanan576cb752021-06-30 21:30:10 +0200212 if system_64bit
213 let b[1008:1015] = 0z00000000.00000000
214 else
215 let b[1008:1011] = 0z00000000
216 endif
James McCoy6654ca72021-06-12 14:05:41 +0200217 call writefile(b, sn)
218 let msg = execute('recover Xfile1')
219 call assert_match('the file has been damaged', msg)
220 call assert_equal('Xfile1', @%)
221 call assert_equal([''], getline(1, '$'))
222 bw!
Yegappan Lakshmanan99285552021-06-06 17:12:46 +0200223
James McCoy6654ca72021-06-12 14:05:41 +0200224 " reduce the page size
225 let b = copy(save_b)
226 let b[12:15] = 0z00010000
227 call writefile(b, sn)
228 let msg = execute('recover Xfile1')
229 call assert_match('page size is smaller than minimum value', msg)
230 call assert_equal('Xfile1', @%)
231 call assert_equal([''], getline(1, '$'))
232 bw!
Yegappan Lakshmanan8cf02e52021-06-07 20:41:22 +0200233
James McCoy6654ca72021-06-12 14:05:41 +0200234 " clear the pointer ID
235 let b = copy(save_b)
236 let b[4096:4097] = 0z0000
237 call writefile(b, sn)
238 call assert_fails('recover Xfile1', 'E310:')
239 call assert_equal('Xfile1', @%)
240 call assert_equal([''], getline(1, '$'))
241 bw!
Yegappan Lakshmanan8cf02e52021-06-07 20:41:22 +0200242
James McCoy6654ca72021-06-12 14:05:41 +0200243 " set the number of pointers in a pointer block to zero
244 let b = copy(save_b)
245 let b[4098:4099] = 0z0000
246 call writefile(b, sn)
247 call assert_fails('recover Xfile1', 'E312:')
248 call assert_equal('Xfile1', @%)
249 call assert_equal(['???EMPTY BLOCK'], getline(1, '$'))
250 bw!
Yegappan Lakshmanan99285552021-06-06 17:12:46 +0200251
Bram Moolenaarb67ba032023-04-22 21:14:26 +0100252 " set the number of pointers in a pointer block to a large value
253 let b = copy(save_b)
254 let b[4098:4099] = 0zFFFF
255 call writefile(b, sn)
256 call assert_fails('recover Xfile1', 'E1364:')
257 call assert_equal('Xfile1', @%)
258 bw!
259
James McCoy6654ca72021-06-12 14:05:41 +0200260 " set the block number in a pointer entry to a negative number
261 let b = copy(save_b)
Yegappan Lakshmanan576cb752021-06-30 21:30:10 +0200262 if system_64bit
263 let b[4104:4111] = little_endian ? 0z00000000.00000080 : 0z80000000.00000000
264 else
265 let b[4104:4107] = little_endian ? 0z00000080 : 0z80000000
266 endif
James McCoy6654ca72021-06-12 14:05:41 +0200267 call writefile(b, sn)
268 call assert_fails('recover Xfile1', 'E312:')
269 call assert_equal('Xfile1', @%)
270 call assert_equal(['???LINES MISSING'], getline(1, '$'))
271 bw!
Yegappan Lakshmanan8cf02e52021-06-07 20:41:22 +0200272
James McCoy6654ca72021-06-12 14:05:41 +0200273 " clear the data block ID
274 let b = copy(save_b)
275 let b[8192:8193] = 0z0000
276 call writefile(b, sn)
277 call assert_fails('recover Xfile1', 'E312:')
278 call assert_equal('Xfile1', @%)
279 call assert_equal(['???BLOCK MISSING'], getline(1, '$'))
280 bw!
Yegappan Lakshmanan8cf02e52021-06-07 20:41:22 +0200281
James McCoy6654ca72021-06-12 14:05:41 +0200282 " set the number of lines in the data block to zero
283 let b = copy(save_b)
Yegappan Lakshmanan576cb752021-06-30 21:30:10 +0200284 if system_64bit
285 let b[8208:8215] = 0z00000000.00000000
286 else
287 let b[8208:8211] = 0z00000000
288 endif
James McCoy6654ca72021-06-12 14:05:41 +0200289 call writefile(b, sn)
290 call assert_fails('recover Xfile1', 'E312:')
291 call assert_equal('Xfile1', @%)
292 call assert_equal(['??? from here until ???END lines may have been inserted/deleted',
293 \ '???END'], getline(1, '$'))
294 bw!
Yegappan Lakshmanan8cf02e52021-06-07 20:41:22 +0200295
James McCoy6654ca72021-06-12 14:05:41 +0200296 " use an invalid text start for the lines in a data block
297 let b = copy(save_b)
Yegappan Lakshmanan576cb752021-06-30 21:30:10 +0200298 if system_64bit
299 let b[8216:8219] = 0z00000000
300 else
301 let b[8212:8215] = 0z00000000
302 endif
James McCoy6654ca72021-06-12 14:05:41 +0200303 call writefile(b, sn)
304 call assert_fails('recover Xfile1', 'E312:')
305 call assert_equal('Xfile1', @%)
306 call assert_equal(['???'], getline(1, '$'))
307 bw!
Yegappan Lakshmanan99285552021-06-06 17:12:46 +0200308
James McCoy6654ca72021-06-12 14:05:41 +0200309 " use an incorrect text end (db_txt_end) for the data block
310 let b = copy(save_b)
311 let b[8204:8207] = little_endian ? 0z80000000 : 0z00000080
312 call writefile(b, sn)
313 call assert_fails('recover Xfile1', 'E312:')
314 call assert_equal('Xfile1', @%)
315 call assert_equal(['??? from here until ???END lines may be messed up', '',
316 \ '???END'], getline(1, '$'))
317 bw!
318
319 " remove the data block
320 let b = copy(save_b)
321 call writefile(b[:8191], sn)
322 call assert_fails('recover Xfile1', 'E312:')
323 call assert_equal('Xfile1', @%)
324 call assert_equal(['???MANY LINES MISSING'], getline(1, '$'))
Yegappan Lakshmanan99285552021-06-06 17:12:46 +0200325
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200326 bw!
327 call delete(sn)
328endfunc
329
330" Test for :recover using an encrypted swap file
331func Test_recover_encrypted_swap_file()
zeertzjq780154b2022-11-17 15:23:52 +0000332 CheckFeature cryptv
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200333 CheckUnix
334
335 " Recover an encrypted file from the swap file without the original file
336 new Xfile1
337 call feedkeys(":X\<CR>vim\<CR>vim\<CR>", 'xt')
338 call setline(1, ['aaa', 'bbb', 'ccc'])
339 preserve
340 let b = readblob('.Xfile1.swp')
341 call writefile(b, '.Xfile1.swm')
342 bw!
343 call feedkeys(":recover Xfile1\<CR>vim\<CR>\<CR>", 'xt')
344 call assert_equal(['aaa', 'bbb', 'ccc'], getline(1, '$'))
345 bw!
346 call delete('.Xfile1.swm')
347
348 " Recover an encrypted file from the swap file with the original file
349 new Xfile1
350 call feedkeys(":X\<CR>vim\<CR>vim\<CR>", 'xt')
351 call setline(1, ['aaa', 'bbb', 'ccc'])
352 update
353 call setline(1, ['111', '222', '333'])
354 preserve
355 let b = readblob('.Xfile1.swp')
356 call writefile(b, '.Xfile1.swm')
357 bw!
358 call feedkeys(":recover Xfile1\<CR>vim\<CR>\<CR>", 'xt')
359 call assert_equal(['111', '222', '333'], getline(1, '$'))
360 call assert_true(&modified)
361 bw!
362 call delete('.Xfile1.swm')
363 call delete('Xfile1')
364endfunc
365
366" Test for :recover using a unreadable swap file
Dominique Pelle923dce22021-11-21 11:36:04 +0000367func Test_recover_unreadable_swap_file()
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200368 CheckUnix
369 CheckNotRoot
370 new Xfile1
371 let b = readblob('.Xfile1.swp')
Bram Moolenaardb77cb32022-10-05 21:45:30 +0100372 call writefile(b, '.Xfile1.swm', 'D')
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200373 bw!
374 call setfperm('.Xfile1.swm', '-w-------')
375 call assert_fails('recover Xfile1', 'E306:')
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200376endfunc
377
378" Test for using :recover when the original file and the swap file have the
379" same contents.
380func Test_recover_unmodified_file()
381 CheckUnix
382 call writefile(['aaa', 'bbb', 'ccc'], 'Xfile1')
383 edit Xfile1
384 preserve
385 let b = readblob('.Xfile1.swp')
386 %bw!
Bram Moolenaardb77cb32022-10-05 21:45:30 +0100387 call writefile(b, '.Xfile1.swz', 'D')
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200388 let msg = execute('recover Xfile1')
389 call assert_equal(['aaa', 'bbb', 'ccc'], getline(1, '$'))
390 call assert_false(&modified)
391 call assert_match('Buffer contents equals file contents', msg)
392 bw!
393 call delete('Xfile1')
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200394endfunc
395
Yegappan Lakshmanan8cf02e52021-06-07 20:41:22 +0200396" Test for recovering a file when editing a symbolically linked file
397func Test_recover_symbolic_link()
398 CheckUnix
Bram Moolenaardb77cb32022-10-05 21:45:30 +0100399 call writefile(['aaa', 'bbb', 'ccc'], 'Xfile1', 'D')
Yegappan Lakshmanan8cf02e52021-06-07 20:41:22 +0200400 silent !ln -s Xfile1 Xfile2
401 edit Xfile2
402 call assert_equal('.Xfile1.swp', fnamemodify(swapname(''), ':t'))
403 preserve
404 let b = readblob('.Xfile1.swp')
405 %bw!
406 call writefile([], 'Xfile1')
407 call writefile(b, '.Xfile1.swp')
408 silent! recover Xfile2
409 call assert_equal(['aaa', 'bbb', 'ccc'], getline(1, '$'))
410 call assert_true(&modified)
411 update
412 %bw!
413 call assert_equal(['aaa', 'bbb', 'ccc'], readfile('Xfile1'))
Yegappan Lakshmanan8cf02e52021-06-07 20:41:22 +0200414 call delete('Xfile2')
415 call delete('.Xfile1.swp')
416endfunc
417
Yegappan Lakshmanan30443242021-06-10 21:52:15 +0200418" 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()
Bram Moolenaardb77cb32022-10-05 21:45:30 +0100422 call writefile([], 'Xfile1', 'D')
Yegappan Lakshmanan30443242021-06-10 21:52:15 +0200423 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')
Bram Moolenaardb77cb32022-10-05 21:45:30 +0100432 call writefile(b, '.Xfile1.swp', 'D')
Yegappan Lakshmanan30443242021-06-10 21:52:15 +0200433 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
Yegappan Lakshmanan30443242021-06-10 21:52:15 +0200444endfunc
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!
Bram Moolenaardb77cb32022-10-05 21:45:30 +0100454 call writefile(b, sn, 'D')
Yegappan Lakshmanan30443242021-06-10 21:52:15 +0200455 exe "recover " .. sn
456 call assert_equal(['one', 'two'], getline(1, '$'))
Yegappan Lakshmanan30443242021-06-10 21:52:15 +0200457endfunc
458
Bram Moolenaar6d91bcb2020-08-12 18:50:36 +0200459" vim: shiftwidth=2 sts=2 expandtab