blob: a084ef463888a4420dfb8a40798cbdfedc90ec69 [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')
Christian Brabandt1c7397f2023-09-05 20:40:25 +0200164 set dir=.
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200165 let msg = execute('recover Xfile1')
166 call assert_match('Unable to read block 0 from .Xfile1.swp', msg)
167 call assert_equal('Xfile1', @%)
168 bw!
Bram Moolenaarf2a8baf2021-09-14 22:58:23 +0200169
170 " make sure there are no old swap files laying around
171 for f in glob('.sw?', 0, 1)
172 call delete(f)
173 endfor
174
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200175 " :recover from an empty buffer
176 call assert_fails('recover', 'E305:')
Christian Brabandt1c7397f2023-09-05 20:40:25 +0200177 set dir&vim
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200178endfunc
179
180" Test for :recover using a corrupted swap file
Yegappan Lakshmanan8cf02e52021-06-07 20:41:22 +0200181" Refer to the comments in the memline.c file for the swap file headers
182" definition.
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200183func Test_recover_corrupted_swap_file()
184 CheckUnix
Yegappan Lakshmanan99285552021-06-06 17:12:46 +0200185
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200186 " recover using a partial swap file
187 call writefile(0z1234, '.Xfile1.swp')
188 call assert_fails('recover Xfile1', 'E295:')
189 bw!
190
191 " recover using invalid content in the swap file
192 call writefile([repeat('1', 2*1024)], '.Xfile1.swp')
193 call assert_fails('recover Xfile1', 'E307:')
194 call delete('.Xfile1.swp')
195
196 " :recover using a swap file with a corrupted header
197 edit Xfile1
198 preserve
199 let sn = swapname('')
200 let b = readblob(sn)
Yegappan Lakshmanan99285552021-06-06 17:12:46 +0200201 let save_b = copy(b)
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200202 bw!
Yegappan Lakshmanan99285552021-06-06 17:12:46 +0200203
James McCoy6654ca72021-06-12 14:05:41 +0200204 " Not all fields are written in a system-independent manner. Detect whether
205 " the test is running on a little or big-endian system, so the correct
206 " corruption values can be set.
James McCoy37f341d2021-10-11 21:04:37 +0100207 " The B0_MAGIC_LONG field may be 32-bit or 64-bit, depending on the system,
208 " even though the value stored is only 32-bits. Therefore, need to check
209 " both the high and low 32-bits to compute these values.
210 let little_endian = (b[1008:1011] == 0z33323130) || (b[1012:1015] == 0z33323130)
211 let system_64bit = little_endian ? (b[1012:1015] == 0z00000000) : (b[1008:1011] == 0z00000000)
Yegappan Lakshmanan8cf02e52021-06-07 20:41:22 +0200212
James McCoy6654ca72021-06-12 14:05:41 +0200213 " clear the B0_MAGIC_LONG field
Yegappan Lakshmanan576cb752021-06-30 21:30:10 +0200214 if system_64bit
215 let b[1008:1015] = 0z00000000.00000000
216 else
217 let b[1008:1011] = 0z00000000
218 endif
James McCoy6654ca72021-06-12 14:05:41 +0200219 call writefile(b, sn)
220 let msg = execute('recover Xfile1')
221 call assert_match('the file has been damaged', msg)
222 call assert_equal('Xfile1', @%)
223 call assert_equal([''], getline(1, '$'))
224 bw!
Yegappan Lakshmanan99285552021-06-06 17:12:46 +0200225
James McCoy6654ca72021-06-12 14:05:41 +0200226 " reduce the page size
227 let b = copy(save_b)
228 let b[12:15] = 0z00010000
229 call writefile(b, sn)
230 let msg = execute('recover Xfile1')
231 call assert_match('page size is smaller than minimum value', msg)
232 call assert_equal('Xfile1', @%)
233 call assert_equal([''], getline(1, '$'))
234 bw!
Yegappan Lakshmanan8cf02e52021-06-07 20:41:22 +0200235
James McCoy6654ca72021-06-12 14:05:41 +0200236 " clear the pointer ID
237 let b = copy(save_b)
238 let b[4096:4097] = 0z0000
239 call writefile(b, sn)
240 call assert_fails('recover Xfile1', 'E310:')
241 call assert_equal('Xfile1', @%)
242 call assert_equal([''], getline(1, '$'))
243 bw!
Yegappan Lakshmanan8cf02e52021-06-07 20:41:22 +0200244
James McCoy6654ca72021-06-12 14:05:41 +0200245 " set the number of pointers in a pointer block to zero
246 let b = copy(save_b)
247 let b[4098:4099] = 0z0000
248 call writefile(b, sn)
249 call assert_fails('recover Xfile1', 'E312:')
250 call assert_equal('Xfile1', @%)
251 call assert_equal(['???EMPTY BLOCK'], getline(1, '$'))
252 bw!
Yegappan Lakshmanan99285552021-06-06 17:12:46 +0200253
Bram Moolenaarb67ba032023-04-22 21:14:26 +0100254 " set the number of pointers in a pointer block to a large value
255 let b = copy(save_b)
256 let b[4098:4099] = 0zFFFF
257 call writefile(b, sn)
258 call assert_fails('recover Xfile1', 'E1364:')
259 call assert_equal('Xfile1', @%)
260 bw!
261
James McCoy6654ca72021-06-12 14:05:41 +0200262 " set the block number in a pointer entry to a negative number
263 let b = copy(save_b)
Yegappan Lakshmanan576cb752021-06-30 21:30:10 +0200264 if system_64bit
265 let b[4104:4111] = little_endian ? 0z00000000.00000080 : 0z80000000.00000000
266 else
267 let b[4104:4107] = little_endian ? 0z00000080 : 0z80000000
268 endif
James McCoy6654ca72021-06-12 14:05:41 +0200269 call writefile(b, sn)
270 call assert_fails('recover Xfile1', 'E312:')
271 call assert_equal('Xfile1', @%)
272 call assert_equal(['???LINES MISSING'], getline(1, '$'))
273 bw!
Yegappan Lakshmanan8cf02e52021-06-07 20:41:22 +0200274
James McCoy6654ca72021-06-12 14:05:41 +0200275 " clear the data block ID
276 let b = copy(save_b)
277 let b[8192:8193] = 0z0000
278 call writefile(b, sn)
279 call assert_fails('recover Xfile1', 'E312:')
280 call assert_equal('Xfile1', @%)
281 call assert_equal(['???BLOCK MISSING'], getline(1, '$'))
282 bw!
Yegappan Lakshmanan8cf02e52021-06-07 20:41:22 +0200283
James McCoy6654ca72021-06-12 14:05:41 +0200284 " set the number of lines in the data block to zero
285 let b = copy(save_b)
Yegappan Lakshmanan576cb752021-06-30 21:30:10 +0200286 if system_64bit
287 let b[8208:8215] = 0z00000000.00000000
288 else
289 let b[8208:8211] = 0z00000000
290 endif
James McCoy6654ca72021-06-12 14:05:41 +0200291 call writefile(b, sn)
292 call assert_fails('recover Xfile1', 'E312:')
293 call assert_equal('Xfile1', @%)
294 call assert_equal(['??? from here until ???END lines may have been inserted/deleted',
295 \ '???END'], getline(1, '$'))
296 bw!
Yegappan Lakshmanan8cf02e52021-06-07 20:41:22 +0200297
Bram Moolenaarbf1b7132023-04-27 21:13:12 +0100298 " set the number of lines in the data block to a large value
299 let b = copy(save_b)
300 if system_64bit
301 let b[8208:8215] = 0z00FFFFFF.FFFFFF00
302 else
303 let b[8208:8211] = 0z00FFFF00
304 endif
305 call writefile(b, sn)
306 call assert_fails('recover Xfile1', 'E312:')
307 call assert_equal('Xfile1', @%)
308 call assert_equal(['??? from here until ???END lines may have been inserted/deleted',
309 \ '', '???', '??? lines may be missing',
310 \ '???END'], getline(1, '$'))
311 bw!
312
James McCoy6654ca72021-06-12 14:05:41 +0200313 " use an invalid text start for the lines in a data block
314 let b = copy(save_b)
Yegappan Lakshmanan576cb752021-06-30 21:30:10 +0200315 if system_64bit
316 let b[8216:8219] = 0z00000000
317 else
318 let b[8212:8215] = 0z00000000
319 endif
James McCoy6654ca72021-06-12 14:05:41 +0200320 call writefile(b, sn)
321 call assert_fails('recover Xfile1', 'E312:')
322 call assert_equal('Xfile1', @%)
323 call assert_equal(['???'], getline(1, '$'))
324 bw!
Yegappan Lakshmanan99285552021-06-06 17:12:46 +0200325
James McCoy6654ca72021-06-12 14:05:41 +0200326 " use an incorrect text end (db_txt_end) for the data block
327 let b = copy(save_b)
328 let b[8204:8207] = little_endian ? 0z80000000 : 0z00000080
329 call writefile(b, sn)
330 call assert_fails('recover Xfile1', 'E312:')
331 call assert_equal('Xfile1', @%)
332 call assert_equal(['??? from here until ???END lines may be messed up', '',
333 \ '???END'], getline(1, '$'))
334 bw!
335
336 " remove the data block
337 let b = copy(save_b)
338 call writefile(b[:8191], sn)
339 call assert_fails('recover Xfile1', 'E312:')
340 call assert_equal('Xfile1', @%)
341 call assert_equal(['???MANY LINES MISSING'], getline(1, '$'))
Yegappan Lakshmanan99285552021-06-06 17:12:46 +0200342
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200343 bw!
344 call delete(sn)
345endfunc
346
347" Test for :recover using an encrypted swap file
348func Test_recover_encrypted_swap_file()
zeertzjq780154b2022-11-17 15:23:52 +0000349 CheckFeature cryptv
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200350 CheckUnix
351
352 " Recover an encrypted file from the swap file without the original file
353 new Xfile1
354 call feedkeys(":X\<CR>vim\<CR>vim\<CR>", 'xt')
355 call setline(1, ['aaa', 'bbb', 'ccc'])
356 preserve
357 let b = readblob('.Xfile1.swp')
358 call writefile(b, '.Xfile1.swm')
359 bw!
360 call feedkeys(":recover Xfile1\<CR>vim\<CR>\<CR>", 'xt')
361 call assert_equal(['aaa', 'bbb', 'ccc'], getline(1, '$'))
362 bw!
363 call delete('.Xfile1.swm')
364
365 " Recover an encrypted file from the swap file with the original file
366 new Xfile1
367 call feedkeys(":X\<CR>vim\<CR>vim\<CR>", 'xt')
368 call setline(1, ['aaa', 'bbb', 'ccc'])
369 update
370 call setline(1, ['111', '222', '333'])
371 preserve
372 let b = readblob('.Xfile1.swp')
373 call writefile(b, '.Xfile1.swm')
374 bw!
375 call feedkeys(":recover Xfile1\<CR>vim\<CR>\<CR>", 'xt')
376 call assert_equal(['111', '222', '333'], getline(1, '$'))
377 call assert_true(&modified)
378 bw!
379 call delete('.Xfile1.swm')
380 call delete('Xfile1')
381endfunc
382
383" Test for :recover using a unreadable swap file
Dominique Pelle923dce22021-11-21 11:36:04 +0000384func Test_recover_unreadable_swap_file()
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200385 CheckUnix
386 CheckNotRoot
387 new Xfile1
388 let b = readblob('.Xfile1.swp')
Bram Moolenaardb77cb32022-10-05 21:45:30 +0100389 call writefile(b, '.Xfile1.swm', 'D')
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200390 bw!
391 call setfperm('.Xfile1.swm', '-w-------')
392 call assert_fails('recover Xfile1', 'E306:')
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200393endfunc
394
395" Test for using :recover when the original file and the swap file have the
396" same contents.
397func Test_recover_unmodified_file()
398 CheckUnix
399 call writefile(['aaa', 'bbb', 'ccc'], 'Xfile1')
400 edit Xfile1
401 preserve
402 let b = readblob('.Xfile1.swp')
403 %bw!
Bram Moolenaardb77cb32022-10-05 21:45:30 +0100404 call writefile(b, '.Xfile1.swz', 'D')
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200405 let msg = execute('recover Xfile1')
406 call assert_equal(['aaa', 'bbb', 'ccc'], getline(1, '$'))
407 call assert_false(&modified)
408 call assert_match('Buffer contents equals file contents', msg)
409 bw!
410 call delete('Xfile1')
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200411endfunc
412
Yegappan Lakshmanan8cf02e52021-06-07 20:41:22 +0200413" Test for recovering a file when editing a symbolically linked file
414func Test_recover_symbolic_link()
415 CheckUnix
Bram Moolenaardb77cb32022-10-05 21:45:30 +0100416 call writefile(['aaa', 'bbb', 'ccc'], 'Xfile1', 'D')
Yegappan Lakshmanan8cf02e52021-06-07 20:41:22 +0200417 silent !ln -s Xfile1 Xfile2
418 edit Xfile2
419 call assert_equal('.Xfile1.swp', fnamemodify(swapname(''), ':t'))
420 preserve
421 let b = readblob('.Xfile1.swp')
422 %bw!
423 call writefile([], 'Xfile1')
424 call writefile(b, '.Xfile1.swp')
425 silent! recover Xfile2
426 call assert_equal(['aaa', 'bbb', 'ccc'], getline(1, '$'))
427 call assert_true(&modified)
428 update
429 %bw!
430 call assert_equal(['aaa', 'bbb', 'ccc'], readfile('Xfile1'))
Yegappan Lakshmanan8cf02e52021-06-07 20:41:22 +0200431 call delete('Xfile2')
432 call delete('.Xfile1.swp')
433endfunc
434
Yegappan Lakshmanan30443242021-06-10 21:52:15 +0200435" Test for recovering a file when an autocmd moves the cursor to an invalid
436" line. This used to result in an internal error (E315) which is fixed
437" by 8.2.2966.
438func Test_recover_invalid_cursor_pos()
Bram Moolenaardb77cb32022-10-05 21:45:30 +0100439 call writefile([], 'Xfile1', 'D')
Yegappan Lakshmanan30443242021-06-10 21:52:15 +0200440 edit Xfile1
441 preserve
442 let b = readblob('.Xfile1.swp')
443 bw!
444 augroup Test
445 au!
446 au BufReadPost Xfile1 normal! 3G
447 augroup END
448 call writefile(range(1, 3), 'Xfile1')
Bram Moolenaardb77cb32022-10-05 21:45:30 +0100449 call writefile(b, '.Xfile1.swp', 'D')
Yegappan Lakshmanan30443242021-06-10 21:52:15 +0200450 try
451 recover Xfile1
452 catch /E308:/
453 " this test is for the :E315 internal error.
454 " ignore the 'E308: Original file may have been changed' error
455 endtry
456 redraw!
457 augroup Test
458 au!
459 augroup END
460 augroup! Test
Yegappan Lakshmanan30443242021-06-10 21:52:15 +0200461endfunc
462
463" Test for recovering a buffer without a name
464func Test_noname_buffer()
465 new
466 call setline(1, ['one', 'two'])
467 preserve
468 let sn = swapname('')
469 let b = readblob(sn)
470 bw!
Bram Moolenaardb77cb32022-10-05 21:45:30 +0100471 call writefile(b, sn, 'D')
Yegappan Lakshmanan30443242021-06-10 21:52:15 +0200472 exe "recover " .. sn
473 call assert_equal(['one', 'two'], getline(1, '$'))
Yegappan Lakshmanan30443242021-06-10 21:52:15 +0200474endfunc
475
Bram Moolenaar6d91bcb2020-08-12 18:50:36 +0200476" vim: shiftwidth=2 sts=2 expandtab