blob: d0129747bb0677182008f97ae54761e5dc5be8a1 [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!
Bram Moolenaarf2a8baf2021-09-14 22:58:23 +0200173
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
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200179 " :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
Yegappan Lakshmanan8cf02e52021-06-07 20:41:22 +0200185" Refer to the comments in the memline.c file for the swap file headers
186" definition.
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200187func Test_recover_corrupted_swap_file()
188 CheckUnix
Yegappan Lakshmanan99285552021-06-06 17:12:46 +0200189
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200190 " 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)
Yegappan Lakshmanan99285552021-06-06 17:12:46 +0200205 let save_b = copy(b)
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200206 bw!
Yegappan Lakshmanan99285552021-06-06 17:12:46 +0200207
James McCoy6654ca72021-06-12 14:05:41 +0200208 " 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.
James McCoy37f341d2021-10-11 21:04:37 +0100211 " 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)
Yegappan Lakshmanan8cf02e52021-06-07 20:41:22 +0200216
James McCoy6654ca72021-06-12 14:05:41 +0200217 " clear the B0_MAGIC_LONG field
Yegappan Lakshmanan576cb752021-06-30 21:30:10 +0200218 if system_64bit
219 let b[1008:1015] = 0z00000000.00000000
220 else
221 let b[1008:1011] = 0z00000000
222 endif
James McCoy6654ca72021-06-12 14:05:41 +0200223 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!
Yegappan Lakshmanan99285552021-06-06 17:12:46 +0200229
James McCoy6654ca72021-06-12 14:05:41 +0200230 " 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!
Yegappan Lakshmanan8cf02e52021-06-07 20:41:22 +0200239
James McCoy6654ca72021-06-12 14:05:41 +0200240 " 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!
Yegappan Lakshmanan8cf02e52021-06-07 20:41:22 +0200248
James McCoy6654ca72021-06-12 14:05:41 +0200249 " 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!
Yegappan Lakshmanan99285552021-06-06 17:12:46 +0200257
James McCoy6654ca72021-06-12 14:05:41 +0200258 " set the block number in a pointer entry to a negative number
259 let b = copy(save_b)
Yegappan Lakshmanan576cb752021-06-30 21:30:10 +0200260 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
James McCoy6654ca72021-06-12 14:05:41 +0200265 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!
Yegappan Lakshmanan8cf02e52021-06-07 20:41:22 +0200270
James McCoy6654ca72021-06-12 14:05:41 +0200271 " 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!
Yegappan Lakshmanan8cf02e52021-06-07 20:41:22 +0200279
James McCoy6654ca72021-06-12 14:05:41 +0200280 " set the number of lines in the data block to zero
281 let b = copy(save_b)
Yegappan Lakshmanan576cb752021-06-30 21:30:10 +0200282 if system_64bit
283 let b[8208:8215] = 0z00000000.00000000
284 else
285 let b[8208:8211] = 0z00000000
286 endif
James McCoy6654ca72021-06-12 14:05:41 +0200287 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!
Yegappan Lakshmanan8cf02e52021-06-07 20:41:22 +0200293
James McCoy6654ca72021-06-12 14:05:41 +0200294 " use an invalid text start for the lines in a data block
295 let b = copy(save_b)
Yegappan Lakshmanan576cb752021-06-30 21:30:10 +0200296 if system_64bit
297 let b[8216:8219] = 0z00000000
298 else
299 let b[8212:8215] = 0z00000000
300 endif
James McCoy6654ca72021-06-12 14:05:41 +0200301 call writefile(b, sn)
302 call assert_fails('recover Xfile1', 'E312:')
303 call assert_equal('Xfile1', @%)
304 call assert_equal(['???'], getline(1, '$'))
305 bw!
Yegappan Lakshmanan99285552021-06-06 17:12:46 +0200306
James McCoy6654ca72021-06-12 14:05:41 +0200307 " 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, '$'))
Yegappan Lakshmanan99285552021-06-06 17:12:46 +0200323
Yegappan Lakshmanan59b26232021-06-05 20:59:22 +0200324 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
Yegappan Lakshmanan8cf02e52021-06-07 20:41:22 +0200395" 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
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()
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
Bram Moolenaar6d91bcb2020-08-12 18:50:36 +0200462" vim: shiftwidth=2 sts=2 expandtab