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 let little_endian = b[1008:1011] == 0z33323130 212 " The swap file header fields can be either 32-bit or 64-bit. 213 let system_64bit = b[1012:1015] == 0z00000000 214 215 " clear the B0_MAGIC_LONG field 216 if system_64bit 217 let b[1008:1015] = 0z00000000.00000000 218 else 219 let b[1008:1011] = 0z00000000 220 endif 221 call writefile(b, sn) 222 let msg = execute('recover Xfile1') 223 call assert_match('the file has been damaged', msg) 224 call assert_equal('Xfile1', @%) 225 call assert_equal([''], getline(1, '$')) 226 bw! 227 228 " reduce the page size 229 let b = copy(save_b) 230 let b[12:15] = 0z00010000 231 call writefile(b, sn) 232 let msg = execute('recover Xfile1') 233 call assert_match('page size is smaller than minimum value', msg) 234 call assert_equal('Xfile1', @%) 235 call assert_equal([''], getline(1, '$')) 236 bw! 237 238 " clear the pointer ID 239 let b = copy(save_b) 240 let b[4096:4097] = 0z0000 241 call writefile(b, sn) 242 call assert_fails('recover Xfile1', 'E310:') 243 call assert_equal('Xfile1', @%) 244 call assert_equal([''], getline(1, '$')) 245 bw! 246 247 " set the number of pointers in a pointer block to zero 248 let b = copy(save_b) 249 let b[4098:4099] = 0z0000 250 call writefile(b, sn) 251 call assert_fails('recover Xfile1', 'E312:') 252 call assert_equal('Xfile1', @%) 253 call assert_equal(['???EMPTY BLOCK'], getline(1, '$')) 254 bw! 255 256 " set the block number in a pointer entry to a negative number 257 let b = copy(save_b) 258 if system_64bit 259 let b[4104:4111] = little_endian ? 0z00000000.00000080 : 0z80000000.00000000 260 else 261 let b[4104:4107] = little_endian ? 0z00000080 : 0z80000000 262 endif 263 call writefile(b, sn) 264 call assert_fails('recover Xfile1', 'E312:') 265 call assert_equal('Xfile1', @%) 266 call assert_equal(['???LINES MISSING'], getline(1, '$')) 267 bw! 268 269 " clear the data block ID 270 let b = copy(save_b) 271 let b[8192:8193] = 0z0000 272 call writefile(b, sn) 273 call assert_fails('recover Xfile1', 'E312:') 274 call assert_equal('Xfile1', @%) 275 call assert_equal(['???BLOCK MISSING'], getline(1, '$')) 276 bw! 277 278 " set the number of lines in the data block to zero 279 let b = copy(save_b) 280 if system_64bit 281 let b[8208:8215] = 0z00000000.00000000 282 else 283 let b[8208:8211] = 0z00000000 284 endif 285 call writefile(b, sn) 286 call assert_fails('recover Xfile1', 'E312:') 287 call assert_equal('Xfile1', @%) 288 call assert_equal(['??? from here until ???END lines may have been inserted/deleted', 289 \ '???END'], getline(1, '$')) 290 bw! 291 292 " use an invalid text start for the lines in a data block 293 let b = copy(save_b) 294 if system_64bit 295 let b[8216:8219] = 0z00000000 296 else 297 let b[8212:8215] = 0z00000000 298 endif 299 call writefile(b, sn) 300 call assert_fails('recover Xfile1', 'E312:') 301 call assert_equal('Xfile1', @%) 302 call assert_equal(['???'], getline(1, '$')) 303 bw! 304 305 " use an incorrect text end (db_txt_end) for the data block 306 let b = copy(save_b) 307 let b[8204:8207] = little_endian ? 0z80000000 : 0z00000080 308 call writefile(b, sn) 309 call assert_fails('recover Xfile1', 'E312:') 310 call assert_equal('Xfile1', @%) 311 call assert_equal(['??? from here until ???END lines may be messed up', '', 312 \ '???END'], getline(1, '$')) 313 bw! 314 315 " remove the data block 316 let b = copy(save_b) 317 call writefile(b[:8191], sn) 318 call assert_fails('recover Xfile1', 'E312:') 319 call assert_equal('Xfile1', @%) 320 call assert_equal(['???MANY LINES MISSING'], getline(1, '$')) 321 322 bw! 323 call delete(sn) 324endfunc 325 326" Test for :recover using an encrypted swap file 327func Test_recover_encrypted_swap_file() 328 CheckUnix 329 330 " Recover an encrypted file from the swap file without the original file 331 new Xfile1 332 call feedkeys(":X\<CR>vim\<CR>vim\<CR>", 'xt') 333 call setline(1, ['aaa', 'bbb', 'ccc']) 334 preserve 335 let b = readblob('.Xfile1.swp') 336 call writefile(b, '.Xfile1.swm') 337 bw! 338 call feedkeys(":recover Xfile1\<CR>vim\<CR>\<CR>", 'xt') 339 call assert_equal(['aaa', 'bbb', 'ccc'], getline(1, '$')) 340 bw! 341 call delete('.Xfile1.swm') 342 343 " Recover an encrypted file from the swap file with the original file 344 new Xfile1 345 call feedkeys(":X\<CR>vim\<CR>vim\<CR>", 'xt') 346 call setline(1, ['aaa', 'bbb', 'ccc']) 347 update 348 call setline(1, ['111', '222', '333']) 349 preserve 350 let b = readblob('.Xfile1.swp') 351 call writefile(b, '.Xfile1.swm') 352 bw! 353 call feedkeys(":recover Xfile1\<CR>vim\<CR>\<CR>", 'xt') 354 call assert_equal(['111', '222', '333'], getline(1, '$')) 355 call assert_true(&modified) 356 bw! 357 call delete('.Xfile1.swm') 358 call delete('Xfile1') 359endfunc 360 361" Test for :recover using a unreadable swap file 362func Test_recover_unreadble_swap_file() 363 CheckUnix 364 CheckNotRoot 365 new Xfile1 366 let b = readblob('.Xfile1.swp') 367 call writefile(b, '.Xfile1.swm') 368 bw! 369 call setfperm('.Xfile1.swm', '-w-------') 370 call assert_fails('recover Xfile1', 'E306:') 371 call delete('.Xfile1.swm') 372endfunc 373 374" Test for using :recover when the original file and the swap file have the 375" same contents. 376func Test_recover_unmodified_file() 377 CheckUnix 378 call writefile(['aaa', 'bbb', 'ccc'], 'Xfile1') 379 edit Xfile1 380 preserve 381 let b = readblob('.Xfile1.swp') 382 %bw! 383 call writefile(b, '.Xfile1.swz') 384 let msg = execute('recover Xfile1') 385 call assert_equal(['aaa', 'bbb', 'ccc'], getline(1, '$')) 386 call assert_false(&modified) 387 call assert_match('Buffer contents equals file contents', msg) 388 bw! 389 call delete('Xfile1') 390 call delete('.Xfile1.swz') 391endfunc 392 393" Test for recovering a file when editing a symbolically linked file 394func Test_recover_symbolic_link() 395 CheckUnix 396 call writefile(['aaa', 'bbb', 'ccc'], 'Xfile1') 397 silent !ln -s Xfile1 Xfile2 398 edit Xfile2 399 call assert_equal('.Xfile1.swp', fnamemodify(swapname(''), ':t')) 400 preserve 401 let b = readblob('.Xfile1.swp') 402 %bw! 403 call writefile([], 'Xfile1') 404 call writefile(b, '.Xfile1.swp') 405 silent! recover Xfile2 406 call assert_equal(['aaa', 'bbb', 'ccc'], getline(1, '$')) 407 call assert_true(&modified) 408 update 409 %bw! 410 call assert_equal(['aaa', 'bbb', 'ccc'], readfile('Xfile1')) 411 call delete('Xfile1') 412 call delete('Xfile2') 413 call delete('.Xfile1.swp') 414endfunc 415 416" Test for recovering a file when an autocmd moves the cursor to an invalid 417" line. This used to result in an internal error (E315) which is fixed 418" by 8.2.2966. 419func Test_recover_invalid_cursor_pos() 420 call writefile([], 'Xfile1') 421 edit Xfile1 422 preserve 423 let b = readblob('.Xfile1.swp') 424 bw! 425 augroup Test 426 au! 427 au BufReadPost Xfile1 normal! 3G 428 augroup END 429 call writefile(range(1, 3), 'Xfile1') 430 call writefile(b, '.Xfile1.swp') 431 try 432 recover Xfile1 433 catch /E308:/ 434 " this test is for the :E315 internal error. 435 " ignore the 'E308: Original file may have been changed' error 436 endtry 437 redraw! 438 augroup Test 439 au! 440 augroup END 441 augroup! Test 442 call delete('Xfile1') 443 call delete('.Xfile1.swp') 444endfunc 445 446" Test for recovering a buffer without a name 447func Test_noname_buffer() 448 new 449 call setline(1, ['one', 'two']) 450 preserve 451 let sn = swapname('') 452 let b = readblob(sn) 453 bw! 454 call writefile(b, sn) 455 exe "recover " .. sn 456 call assert_equal(['one', 'two'], getline(1, '$')) 457 call delete(sn) 458endfunc 459 460" vim: shiftwidth=2 sts=2 expandtab 461