1" Test :recover 2 3source check.vim 4 5func 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 12 if has('win32') || filewritable('/') == 2 13 " can write in / directory on MS-Windows 14 set dir=/notexist/ 15 endif 16 call assert_fails('split Xtest', 'E303:') 17 18 " No error with empty 'directory' setting. 19 set directory= 20 split XtestOK 21 close! 22 23 set dir& 24endfunc 25 26" 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 41" 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() 47 set fileformat=unix undolevels=-1 48 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 57 58 let swname = CopySwapfile() 59 60 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 80 81func Test_nocatch_process_still_running() 82 " sysinfo.uptime probably only works on Linux 83 if !has('linux') 84 let g:skipped_reason = 'only works on Linux' 85 return 86 endif 87 " the GUI dialog can't be handled 88 if has('gui_running') 89 let g:skipped_reason = 'only works in the terminal' 90 return 91 endif 92 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 136" 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 142 let b = readblob(swapname('')) 143 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, '$')) 149 " 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! 159 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 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 179 " :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 185" Refer to the comments in the memline.c file for the swap file headers 186" definition. 187func Test_recover_corrupted_swap_file() 188 CheckUnix 189 190 " 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) 205 let save_b = copy(b) 206 bw! 207 208 " 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. 211 " 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) 216 217 " clear the B0_MAGIC_LONG field 218 if system_64bit 219 let b[1008:1015] = 0z00000000.00000000 220 else 221 let b[1008:1011] = 0z00000000 222 endif 223 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! 229 230 " 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! 239 240 " 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! 248 249 " 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! 257 258 " set the block number in a pointer entry to a negative number 259 let b = copy(save_b) 260 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 265 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! 270 271 " 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! 279 280 " set the number of lines in the data block to zero 281 let b = copy(save_b) 282 if system_64bit 283 let b[8208:8215] = 0z00000000.00000000 284 else 285 let b[8208:8211] = 0z00000000 286 endif 287 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! 293 294 " use an invalid text start for the lines in a data block 295 let b = copy(save_b) 296 if system_64bit 297 let b[8216:8219] = 0z00000000 298 else 299 let b[8212:8215] = 0z00000000 300 endif 301 call writefile(b, sn) 302 call assert_fails('recover Xfile1', 'E312:') 303 call assert_equal('Xfile1', @%) 304 call assert_equal(['???'], getline(1, '$')) 305 bw! 306 307 " 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, '$')) 323 324 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 395" 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 418" 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 462" vim: shiftwidth=2 sts=2 expandtab 463