blob: 6ef4376defea07bf20c7789c4f4c964e3868289c [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(''))
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200143 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, '$'))
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 +0200159
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 " :recover from an empty buffer
174 call assert_fails('recover', 'E305:')
175 call delete('.Xfile1.swp')
176endfunc
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.
Yegappan Lakshmanan576cb752021-06-30 21:30:10 +0200205 let little_endian = b[1008:1011] == 0z33323130
206 " The swap file header fields can be either 32-bit or 64-bit.
207 let system_64bit = b[1012:1015] == 0z00000000
Yegappan Lakshmanan8cf02e52021-06-07 20:41:22 +0200208
James McCoy6654ca72021-06-12 14:05:41 +0200209 " clear the B0_MAGIC_LONG field
Yegappan Lakshmanan576cb752021-06-30 21:30:10 +0200210 if system_64bit
211 let b[1008:1015] = 0z00000000.00000000
212 else
213 let b[1008:1011] = 0z00000000
214 endif
James McCoy6654ca72021-06-12 14:05:41 +0200215 call writefile(b, sn)
216 let msg = execute('recover Xfile1')
217 call assert_match('the file has been damaged', msg)
218 call assert_equal('Xfile1', @%)
219 call assert_equal([''], getline(1, '$'))
220 bw!
Yegappan Lakshmanan99285552021-06-06 17:12:46 +0200221
James McCoy6654ca72021-06-12 14:05:41 +0200222 " reduce the page size
223 let b = copy(save_b)
224 let b[12:15] = 0z00010000
225 call writefile(b, sn)
226 let msg = execute('recover Xfile1')
227 call assert_match('page size is smaller than minimum value', msg)
228 call assert_equal('Xfile1', @%)
229 call assert_equal([''], getline(1, '$'))
230 bw!
Yegappan Lakshmanan8cf02e52021-06-07 20:41:22 +0200231
James McCoy6654ca72021-06-12 14:05:41 +0200232 " clear the pointer ID
233 let b = copy(save_b)
234 let b[4096:4097] = 0z0000
235 call writefile(b, sn)
236 call assert_fails('recover Xfile1', 'E310:')
237 call assert_equal('Xfile1', @%)
238 call assert_equal([''], getline(1, '$'))
239 bw!
Yegappan Lakshmanan8cf02e52021-06-07 20:41:22 +0200240
James McCoy6654ca72021-06-12 14:05:41 +0200241 " set the number of pointers in a pointer block to zero
242 let b = copy(save_b)
243 let b[4098:4099] = 0z0000
244 call writefile(b, sn)
245 call assert_fails('recover Xfile1', 'E312:')
246 call assert_equal('Xfile1', @%)
247 call assert_equal(['???EMPTY BLOCK'], getline(1, '$'))
248 bw!
Yegappan Lakshmanan99285552021-06-06 17:12:46 +0200249
James McCoy6654ca72021-06-12 14:05:41 +0200250 " set the block number in a pointer entry to a negative number
251 let b = copy(save_b)
Yegappan Lakshmanan576cb752021-06-30 21:30:10 +0200252 if system_64bit
253 let b[4104:4111] = little_endian ? 0z00000000.00000080 : 0z80000000.00000000
254 else
255 let b[4104:4107] = little_endian ? 0z00000080 : 0z80000000
256 endif
James McCoy6654ca72021-06-12 14:05:41 +0200257 call writefile(b, sn)
258 call assert_fails('recover Xfile1', 'E312:')
259 call assert_equal('Xfile1', @%)
260 call assert_equal(['???LINES MISSING'], getline(1, '$'))
261 bw!
Yegappan Lakshmanan8cf02e52021-06-07 20:41:22 +0200262
James McCoy6654ca72021-06-12 14:05:41 +0200263 " clear the data block ID
264 let b = copy(save_b)
265 let b[8192:8193] = 0z0000
266 call writefile(b, sn)
267 call assert_fails('recover Xfile1', 'E312:')
268 call assert_equal('Xfile1', @%)
269 call assert_equal(['???BLOCK MISSING'], getline(1, '$'))
270 bw!
Yegappan Lakshmanan8cf02e52021-06-07 20:41:22 +0200271
James McCoy6654ca72021-06-12 14:05:41 +0200272 " set the number of lines in the data block to zero
273 let b = copy(save_b)
Yegappan Lakshmanan576cb752021-06-30 21:30:10 +0200274 if system_64bit
275 let b[8208:8215] = 0z00000000.00000000
276 else
277 let b[8208:8211] = 0z00000000
278 endif
James McCoy6654ca72021-06-12 14:05:41 +0200279 call writefile(b, sn)
280 call assert_fails('recover Xfile1', 'E312:')
281 call assert_equal('Xfile1', @%)
282 call assert_equal(['??? from here until ???END lines may have been inserted/deleted',
283 \ '???END'], getline(1, '$'))
284 bw!
Yegappan Lakshmanan8cf02e52021-06-07 20:41:22 +0200285
James McCoy6654ca72021-06-12 14:05:41 +0200286 " use an invalid text start for the lines in a data block
287 let b = copy(save_b)
Yegappan Lakshmanan576cb752021-06-30 21:30:10 +0200288 if system_64bit
289 let b[8216:8219] = 0z00000000
290 else
291 let b[8212:8215] = 0z00000000
292 endif
James McCoy6654ca72021-06-12 14:05:41 +0200293 call writefile(b, sn)
294 call assert_fails('recover Xfile1', 'E312:')
295 call assert_equal('Xfile1', @%)
296 call assert_equal(['???'], getline(1, '$'))
297 bw!
Yegappan Lakshmanan99285552021-06-06 17:12:46 +0200298
James McCoy6654ca72021-06-12 14:05:41 +0200299 " use an incorrect text end (db_txt_end) for the data block
300 let b = copy(save_b)
301 let b[8204:8207] = little_endian ? 0z80000000 : 0z00000080
302 call writefile(b, sn)
303 call assert_fails('recover Xfile1', 'E312:')
304 call assert_equal('Xfile1', @%)
305 call assert_equal(['??? from here until ???END lines may be messed up', '',
306 \ '???END'], getline(1, '$'))
307 bw!
308
309 " remove the data block
310 let b = copy(save_b)
311 call writefile(b[:8191], sn)
312 call assert_fails('recover Xfile1', 'E312:')
313 call assert_equal('Xfile1', @%)
314 call assert_equal(['???MANY LINES MISSING'], getline(1, '$'))
Yegappan Lakshmanan99285552021-06-06 17:12:46 +0200315
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200316 bw!
317 call delete(sn)
318endfunc
319
320" Test for :recover using an encrypted swap file
321func Test_recover_encrypted_swap_file()
322 CheckUnix
323
324 " Recover an encrypted file from the swap file without the original file
325 new Xfile1
326 call feedkeys(":X\<CR>vim\<CR>vim\<CR>", 'xt')
327 call setline(1, ['aaa', 'bbb', 'ccc'])
328 preserve
329 let b = readblob('.Xfile1.swp')
330 call writefile(b, '.Xfile1.swm')
331 bw!
332 call feedkeys(":recover Xfile1\<CR>vim\<CR>\<CR>", 'xt')
333 call assert_equal(['aaa', 'bbb', 'ccc'], getline(1, '$'))
334 bw!
335 call delete('.Xfile1.swm')
336
337 " Recover an encrypted file from the swap file with the original file
338 new Xfile1
339 call feedkeys(":X\<CR>vim\<CR>vim\<CR>", 'xt')
340 call setline(1, ['aaa', 'bbb', 'ccc'])
341 update
342 call setline(1, ['111', '222', '333'])
343 preserve
344 let b = readblob('.Xfile1.swp')
345 call writefile(b, '.Xfile1.swm')
346 bw!
347 call feedkeys(":recover Xfile1\<CR>vim\<CR>\<CR>", 'xt')
348 call assert_equal(['111', '222', '333'], getline(1, '$'))
349 call assert_true(&modified)
350 bw!
351 call delete('.Xfile1.swm')
352 call delete('Xfile1')
353endfunc
354
355" Test for :recover using a unreadable swap file
356func Test_recover_unreadble_swap_file()
357 CheckUnix
358 CheckNotRoot
359 new Xfile1
360 let b = readblob('.Xfile1.swp')
361 call writefile(b, '.Xfile1.swm')
362 bw!
363 call setfperm('.Xfile1.swm', '-w-------')
364 call assert_fails('recover Xfile1', 'E306:')
365 call delete('.Xfile1.swm')
366endfunc
367
368" Test for using :recover when the original file and the swap file have the
369" same contents.
370func Test_recover_unmodified_file()
371 CheckUnix
372 call writefile(['aaa', 'bbb', 'ccc'], 'Xfile1')
373 edit Xfile1
374 preserve
375 let b = readblob('.Xfile1.swp')
376 %bw!
377 call writefile(b, '.Xfile1.swz')
378 let msg = execute('recover Xfile1')
379 call assert_equal(['aaa', 'bbb', 'ccc'], getline(1, '$'))
380 call assert_false(&modified)
381 call assert_match('Buffer contents equals file contents', msg)
382 bw!
383 call delete('Xfile1')
384 call delete('.Xfile1.swz')
385endfunc
386
Yegappan Lakshmanan8cf02e52021-06-07 20:41:22 +0200387" Test for recovering a file when editing a symbolically linked file
388func Test_recover_symbolic_link()
389 CheckUnix
390 call writefile(['aaa', 'bbb', 'ccc'], 'Xfile1')
391 silent !ln -s Xfile1 Xfile2
392 edit Xfile2
393 call assert_equal('.Xfile1.swp', fnamemodify(swapname(''), ':t'))
394 preserve
395 let b = readblob('.Xfile1.swp')
396 %bw!
397 call writefile([], 'Xfile1')
398 call writefile(b, '.Xfile1.swp')
399 silent! recover Xfile2
400 call assert_equal(['aaa', 'bbb', 'ccc'], getline(1, '$'))
401 call assert_true(&modified)
402 update
403 %bw!
404 call assert_equal(['aaa', 'bbb', 'ccc'], readfile('Xfile1'))
405 call delete('Xfile1')
406 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()
414 call writefile([], 'Xfile1')
415 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')
424 call writefile(b, '.Xfile1.swp')
425 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
436 call delete('Xfile1')
437 call delete('.Xfile1.swp')
438endfunc
439
440" Test for recovering a buffer without a name
441func Test_noname_buffer()
442 new
443 call setline(1, ['one', 'two'])
444 preserve
445 let sn = swapname('')
446 let b = readblob(sn)
447 bw!
448 call writefile(b, sn)
449 exe "recover " .. sn
450 call assert_equal(['one', 'two'], getline(1, '$'))
451 call delete(sn)
452endfunc
453
Bram Moolenaar6d91bcb2020-08-12 18:50:36 +0200454" vim: shiftwidth=2 sts=2 expandtab