blob: cb34c64dd2db9b6fb221811120acbcd71c0d4470 [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
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
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200136" 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
Yegappan Lakshmanan99285552021-06-06 17:12:46 +0200142 let b = readblob(swapname(''))
Bram Moolenaardb77cb32022-10-05 21:45:30 +0100143 call writefile(b, '.Xfile1.swm', 'D')
144 call writefile(b, '.Xfile1.swn', 'D')
145 call writefile(b, '.Xfile1.swo', 'D')
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200146 %bw!
147 call feedkeys(":recover Xfile1\<CR>3\<CR>q", 'xt')
148 call assert_equal(['a', 'b', 'c'], getline(1, '$'))
Yegappan Lakshmanan8cf02e52021-06-07 20:41:22 +0200149 " 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!
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200159endfunc
160
161" Test for :recover using an empty swap file
162func Test_recover_empty_swap_file()
163 CheckUnix
Bram Moolenaardb77cb32022-10-05 21:45:30 +0100164 call writefile([], '.Xfile1.swp', 'D')
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:')
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200177endfunc
178
179" Test for :recover using a corrupted swap file
Yegappan Lakshmanan8cf02e52021-06-07 20:41:22 +0200180" Refer to the comments in the memline.c file for the swap file headers
181" definition.
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200182func Test_recover_corrupted_swap_file()
183 CheckUnix
Yegappan Lakshmanan99285552021-06-06 17:12:46 +0200184
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200185 " recover using a partial swap file
186 call writefile(0z1234, '.Xfile1.swp')
187 call assert_fails('recover Xfile1', 'E295:')
188 bw!
189
190 " recover using invalid content in the swap file
191 call writefile([repeat('1', 2*1024)], '.Xfile1.swp')
192 call assert_fails('recover Xfile1', 'E307:')
193 call delete('.Xfile1.swp')
194
195 " :recover using a swap file with a corrupted header
196 edit Xfile1
197 preserve
198 let sn = swapname('')
199 let b = readblob(sn)
Yegappan Lakshmanan99285552021-06-06 17:12:46 +0200200 let save_b = copy(b)
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200201 bw!
Yegappan Lakshmanan99285552021-06-06 17:12:46 +0200202
James McCoy6654ca72021-06-12 14:05:41 +0200203 " Not all fields are written in a system-independent manner. Detect whether
204 " the test is running on a little or big-endian system, so the correct
205 " corruption values can be set.
James McCoy37f341d2021-10-11 21:04:37 +0100206 " The B0_MAGIC_LONG field may be 32-bit or 64-bit, depending on the system,
207 " even though the value stored is only 32-bits. Therefore, need to check
208 " both the high and low 32-bits to compute these values.
209 let little_endian = (b[1008:1011] == 0z33323130) || (b[1012:1015] == 0z33323130)
210 let system_64bit = little_endian ? (b[1012:1015] == 0z00000000) : (b[1008:1011] == 0z00000000)
Yegappan Lakshmanan8cf02e52021-06-07 20:41:22 +0200211
James McCoy6654ca72021-06-12 14:05:41 +0200212 " clear the B0_MAGIC_LONG field
Yegappan Lakshmanan576cb752021-06-30 21:30:10 +0200213 if system_64bit
214 let b[1008:1015] = 0z00000000.00000000
215 else
216 let b[1008:1011] = 0z00000000
217 endif
James McCoy6654ca72021-06-12 14:05:41 +0200218 call writefile(b, sn)
219 let msg = execute('recover Xfile1')
220 call assert_match('the file has been damaged', msg)
221 call assert_equal('Xfile1', @%)
222 call assert_equal([''], getline(1, '$'))
223 bw!
Yegappan Lakshmanan99285552021-06-06 17:12:46 +0200224
James McCoy6654ca72021-06-12 14:05:41 +0200225 " reduce the page size
226 let b = copy(save_b)
227 let b[12:15] = 0z00010000
228 call writefile(b, sn)
229 let msg = execute('recover Xfile1')
230 call assert_match('page size is smaller than minimum value', msg)
231 call assert_equal('Xfile1', @%)
232 call assert_equal([''], getline(1, '$'))
233 bw!
Yegappan Lakshmanan8cf02e52021-06-07 20:41:22 +0200234
James McCoy6654ca72021-06-12 14:05:41 +0200235 " clear the pointer ID
236 let b = copy(save_b)
237 let b[4096:4097] = 0z0000
238 call writefile(b, sn)
239 call assert_fails('recover Xfile1', 'E310:')
240 call assert_equal('Xfile1', @%)
241 call assert_equal([''], getline(1, '$'))
242 bw!
Yegappan Lakshmanan8cf02e52021-06-07 20:41:22 +0200243
James McCoy6654ca72021-06-12 14:05:41 +0200244 " set the number of pointers in a pointer block to zero
245 let b = copy(save_b)
246 let b[4098:4099] = 0z0000
247 call writefile(b, sn)
248 call assert_fails('recover Xfile1', 'E312:')
249 call assert_equal('Xfile1', @%)
250 call assert_equal(['???EMPTY BLOCK'], getline(1, '$'))
251 bw!
Yegappan Lakshmanan99285552021-06-06 17:12:46 +0200252
James McCoy6654ca72021-06-12 14:05:41 +0200253 " set the block number in a pointer entry to a negative number
254 let b = copy(save_b)
Yegappan Lakshmanan576cb752021-06-30 21:30:10 +0200255 if system_64bit
256 let b[4104:4111] = little_endian ? 0z00000000.00000080 : 0z80000000.00000000
257 else
258 let b[4104:4107] = little_endian ? 0z00000080 : 0z80000000
259 endif
James McCoy6654ca72021-06-12 14:05:41 +0200260 call writefile(b, sn)
261 call assert_fails('recover Xfile1', 'E312:')
262 call assert_equal('Xfile1', @%)
263 call assert_equal(['???LINES MISSING'], getline(1, '$'))
264 bw!
Yegappan Lakshmanan8cf02e52021-06-07 20:41:22 +0200265
James McCoy6654ca72021-06-12 14:05:41 +0200266 " clear the data block ID
267 let b = copy(save_b)
268 let b[8192:8193] = 0z0000
269 call writefile(b, sn)
270 call assert_fails('recover Xfile1', 'E312:')
271 call assert_equal('Xfile1', @%)
272 call assert_equal(['???BLOCK MISSING'], getline(1, '$'))
273 bw!
Yegappan Lakshmanan8cf02e52021-06-07 20:41:22 +0200274
James McCoy6654ca72021-06-12 14:05:41 +0200275 " set the number of lines in the data block to zero
276 let b = copy(save_b)
Yegappan Lakshmanan576cb752021-06-30 21:30:10 +0200277 if system_64bit
278 let b[8208:8215] = 0z00000000.00000000
279 else
280 let b[8208:8211] = 0z00000000
281 endif
James McCoy6654ca72021-06-12 14:05:41 +0200282 call writefile(b, sn)
283 call assert_fails('recover Xfile1', 'E312:')
284 call assert_equal('Xfile1', @%)
285 call assert_equal(['??? from here until ???END lines may have been inserted/deleted',
286 \ '???END'], getline(1, '$'))
287 bw!
Yegappan Lakshmanan8cf02e52021-06-07 20:41:22 +0200288
James McCoy6654ca72021-06-12 14:05:41 +0200289 " use an invalid text start for the lines in a data block
290 let b = copy(save_b)
Yegappan Lakshmanan576cb752021-06-30 21:30:10 +0200291 if system_64bit
292 let b[8216:8219] = 0z00000000
293 else
294 let b[8212:8215] = 0z00000000
295 endif
James McCoy6654ca72021-06-12 14:05:41 +0200296 call writefile(b, sn)
297 call assert_fails('recover Xfile1', 'E312:')
298 call assert_equal('Xfile1', @%)
299 call assert_equal(['???'], getline(1, '$'))
300 bw!
Yegappan Lakshmanan99285552021-06-06 17:12:46 +0200301
James McCoy6654ca72021-06-12 14:05:41 +0200302 " use an incorrect text end (db_txt_end) for the data block
303 let b = copy(save_b)
304 let b[8204:8207] = little_endian ? 0z80000000 : 0z00000080
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 be messed up', '',
309 \ '???END'], getline(1, '$'))
310 bw!
311
312 " remove the data block
313 let b = copy(save_b)
314 call writefile(b[:8191], sn)
315 call assert_fails('recover Xfile1', 'E312:')
316 call assert_equal('Xfile1', @%)
317 call assert_equal(['???MANY LINES MISSING'], getline(1, '$'))
Yegappan Lakshmanan99285552021-06-06 17:12:46 +0200318
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200319 bw!
320 call delete(sn)
321endfunc
322
323" Test for :recover using an encrypted swap file
324func Test_recover_encrypted_swap_file()
325 CheckUnix
326
327 " Recover an encrypted file from the swap file without the original file
328 new Xfile1
329 call feedkeys(":X\<CR>vim\<CR>vim\<CR>", 'xt')
330 call setline(1, ['aaa', 'bbb', 'ccc'])
331 preserve
332 let b = readblob('.Xfile1.swp')
333 call writefile(b, '.Xfile1.swm')
334 bw!
335 call feedkeys(":recover Xfile1\<CR>vim\<CR>\<CR>", 'xt')
336 call assert_equal(['aaa', 'bbb', 'ccc'], getline(1, '$'))
337 bw!
338 call delete('.Xfile1.swm')
339
340 " Recover an encrypted file from the swap file with the original file
341 new Xfile1
342 call feedkeys(":X\<CR>vim\<CR>vim\<CR>", 'xt')
343 call setline(1, ['aaa', 'bbb', 'ccc'])
344 update
345 call setline(1, ['111', '222', '333'])
346 preserve
347 let b = readblob('.Xfile1.swp')
348 call writefile(b, '.Xfile1.swm')
349 bw!
350 call feedkeys(":recover Xfile1\<CR>vim\<CR>\<CR>", 'xt')
351 call assert_equal(['111', '222', '333'], getline(1, '$'))
352 call assert_true(&modified)
353 bw!
354 call delete('.Xfile1.swm')
355 call delete('Xfile1')
356endfunc
357
358" Test for :recover using a unreadable swap file
Dominique Pelle923dce22021-11-21 11:36:04 +0000359func Test_recover_unreadable_swap_file()
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200360 CheckUnix
361 CheckNotRoot
362 new Xfile1
363 let b = readblob('.Xfile1.swp')
Bram Moolenaardb77cb32022-10-05 21:45:30 +0100364 call writefile(b, '.Xfile1.swm', 'D')
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200365 bw!
366 call setfperm('.Xfile1.swm', '-w-------')
367 call assert_fails('recover Xfile1', 'E306:')
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200368endfunc
369
370" Test for using :recover when the original file and the swap file have the
371" same contents.
372func Test_recover_unmodified_file()
373 CheckUnix
374 call writefile(['aaa', 'bbb', 'ccc'], 'Xfile1')
375 edit Xfile1
376 preserve
377 let b = readblob('.Xfile1.swp')
378 %bw!
Bram Moolenaardb77cb32022-10-05 21:45:30 +0100379 call writefile(b, '.Xfile1.swz', 'D')
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200380 let msg = execute('recover Xfile1')
381 call assert_equal(['aaa', 'bbb', 'ccc'], getline(1, '$'))
382 call assert_false(&modified)
383 call assert_match('Buffer contents equals file contents', msg)
384 bw!
385 call delete('Xfile1')
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200386endfunc
387
Yegappan Lakshmanan8cf02e52021-06-07 20:41:22 +0200388" Test for recovering a file when editing a symbolically linked file
389func Test_recover_symbolic_link()
390 CheckUnix
Bram Moolenaardb77cb32022-10-05 21:45:30 +0100391 call writefile(['aaa', 'bbb', 'ccc'], 'Xfile1', 'D')
Yegappan Lakshmanan8cf02e52021-06-07 20:41:22 +0200392 silent !ln -s Xfile1 Xfile2
393 edit Xfile2
394 call assert_equal('.Xfile1.swp', fnamemodify(swapname(''), ':t'))
395 preserve
396 let b = readblob('.Xfile1.swp')
397 %bw!
398 call writefile([], 'Xfile1')
399 call writefile(b, '.Xfile1.swp')
400 silent! recover Xfile2
401 call assert_equal(['aaa', 'bbb', 'ccc'], getline(1, '$'))
402 call assert_true(&modified)
403 update
404 %bw!
405 call assert_equal(['aaa', 'bbb', 'ccc'], readfile('Xfile1'))
Yegappan Lakshmanan8cf02e52021-06-07 20:41:22 +0200406 call delete('Xfile2')
407 call delete('.Xfile1.swp')
408endfunc
409
Yegappan Lakshmanan30443242021-06-10 21:52:15 +0200410" Test for recovering a file when an autocmd moves the cursor to an invalid
411" line. This used to result in an internal error (E315) which is fixed
412" by 8.2.2966.
413func Test_recover_invalid_cursor_pos()
Bram Moolenaardb77cb32022-10-05 21:45:30 +0100414 call writefile([], 'Xfile1', 'D')
Yegappan Lakshmanan30443242021-06-10 21:52:15 +0200415 edit Xfile1
416 preserve
417 let b = readblob('.Xfile1.swp')
418 bw!
419 augroup Test
420 au!
421 au BufReadPost Xfile1 normal! 3G
422 augroup END
423 call writefile(range(1, 3), 'Xfile1')
Bram Moolenaardb77cb32022-10-05 21:45:30 +0100424 call writefile(b, '.Xfile1.swp', 'D')
Yegappan Lakshmanan30443242021-06-10 21:52:15 +0200425 try
426 recover Xfile1
427 catch /E308:/
428 " this test is for the :E315 internal error.
429 " ignore the 'E308: Original file may have been changed' error
430 endtry
431 redraw!
432 augroup Test
433 au!
434 augroup END
435 augroup! Test
Yegappan Lakshmanan30443242021-06-10 21:52:15 +0200436endfunc
437
438" Test for recovering a buffer without a name
439func Test_noname_buffer()
440 new
441 call setline(1, ['one', 'two'])
442 preserve
443 let sn = swapname('')
444 let b = readblob(sn)
445 bw!
Bram Moolenaardb77cb32022-10-05 21:45:30 +0100446 call writefile(b, sn, 'D')
Yegappan Lakshmanan30443242021-06-10 21:52:15 +0200447 exe "recover " .. sn
448 call assert_equal(['one', 'two'], getline(1, '$'))
Yegappan Lakshmanan30443242021-06-10 21:52:15 +0200449endfunc
450
Bram Moolenaar6d91bcb2020-08-12 18:50:36 +0200451" vim: shiftwidth=2 sts=2 expandtab