1" Tests for the undo tree. 2" Since this script is sourced we need to explicitly break changes up in 3" undo-able pieces. Do that by setting 'undolevels'. 4" Also tests :earlier and :later. 5 6func Test_undotree() 7 new 8 9 normal! Aabc 10 set ul=100 11 let d = undotree() 12 call assert_equal(1, d.seq_last) 13 call assert_equal(1, d.seq_cur) 14 call assert_equal(0, d.save_last) 15 call assert_equal(0, d.save_cur) 16 call assert_equal(1, len(d.entries)) 17 call assert_equal(1, d.entries[0].newhead) 18 call assert_equal(1, d.entries[0].seq) 19 call assert_true(d.entries[0].time <= d.time_cur) 20 21 normal! Adef 22 set ul=100 23 let d = undotree() 24 call assert_equal(2, d.seq_last) 25 call assert_equal(2, d.seq_cur) 26 call assert_equal(0, d.save_last) 27 call assert_equal(0, d.save_cur) 28 call assert_equal(2, len(d.entries)) 29 call assert_equal(1, d.entries[0].seq) 30 call assert_equal(1, d.entries[1].newhead) 31 call assert_equal(2, d.entries[1].seq) 32 call assert_true(d.entries[1].time <= d.time_cur) 33 34 undo 35 set ul=100 36 let d = undotree() 37 call assert_equal(2, d.seq_last) 38 call assert_equal(1, d.seq_cur) 39 call assert_equal(0, d.save_last) 40 call assert_equal(0, d.save_cur) 41 call assert_equal(2, len(d.entries)) 42 call assert_equal(1, d.entries[0].seq) 43 call assert_equal(1, d.entries[1].curhead) 44 call assert_equal(1, d.entries[1].newhead) 45 call assert_equal(2, d.entries[1].seq) 46 call assert_true(d.entries[1].time == d.time_cur) 47 48 normal! Aghi 49 set ul=100 50 let d = undotree() 51 call assert_equal(3, d.seq_last) 52 call assert_equal(3, d.seq_cur) 53 call assert_equal(0, d.save_last) 54 call assert_equal(0, d.save_cur) 55 call assert_equal(2, len(d.entries)) 56 call assert_equal(1, d.entries[0].seq) 57 call assert_equal(2, d.entries[1].alt[0].seq) 58 call assert_equal(1, d.entries[1].newhead) 59 call assert_equal(3, d.entries[1].seq) 60 call assert_true(d.entries[1].time <= d.time_cur) 61 62 undo 63 set ul=100 64 let d = undotree() 65 call assert_equal(3, d.seq_last) 66 call assert_equal(1, d.seq_cur) 67 call assert_equal(0, d.save_last) 68 call assert_equal(0, d.save_cur) 69 call assert_equal(2, len(d.entries)) 70 call assert_equal(1, d.entries[0].seq) 71 call assert_equal(2, d.entries[1].alt[0].seq) 72 call assert_equal(1, d.entries[1].curhead) 73 call assert_equal(1, d.entries[1].newhead) 74 call assert_equal(3, d.entries[1].seq) 75 call assert_true(d.entries[1].time == d.time_cur) 76 77 w! Xtest 78 let d = undotree() 79 call assert_equal(1, d.save_cur) 80 call assert_equal(1, d.save_last) 81 call delete('Xtest') 82 bwipe! Xtest 83endfunc 84 85func FillBuffer() 86 for i in range(1,13) 87 put=i 88 " Set 'undolevels' to split undo. 89 exe "setg ul=" . &g:ul 90 endfor 91endfunc 92 93func Test_global_local_undolevels() 94 new one 95 set undolevels=5 96 call FillBuffer() 97 " will only undo the last 5 changes, end up with 13 - (5 + 1) = 7 lines 98 earlier 10 99 call assert_equal(5, &g:undolevels) 100 call assert_equal(-123456, &l:undolevels) 101 call assert_equal('7', getline('$')) 102 103 new two 104 setlocal undolevels=2 105 call FillBuffer() 106 " will only undo the last 2 changes, end up with 13 - (2 + 1) = 10 lines 107 earlier 10 108 call assert_equal(5, &g:undolevels) 109 call assert_equal(2, &l:undolevels) 110 call assert_equal('10', getline('$')) 111 112 setlocal ul=10 113 call assert_equal(5, &g:undolevels) 114 call assert_equal(10, &l:undolevels) 115 116 " Setting local value in "two" must not change local value in "one" 117 wincmd p 118 call assert_equal(5, &g:undolevels) 119 call assert_equal(-123456, &l:undolevels) 120 121 new three 122 setglobal ul=50 123 call assert_equal(50, &g:undolevels) 124 call assert_equal(-123456, &l:undolevels) 125 126 " Drop created windows 127 set ul& 128 new 129 only! 130endfunc 131 132func BackOne(expected) 133 call feedkeys('g-', 'xt') 134 call assert_equal(a:expected, getline(1)) 135endfunc 136 137func Test_undo_del_chars() 138 " Setup a buffer without creating undo entries 139 new 140 set ul=-1 141 call setline(1, ['123-456']) 142 set ul=100 143 1 144 call test_settime(100) 145 146 " Delete three characters and undo with g- 147 call feedkeys('x', 'xt') 148 call feedkeys('x', 'xt') 149 call feedkeys('x', 'xt') 150 call assert_equal('-456', getline(1)) 151 call BackOne('3-456') 152 call BackOne('23-456') 153 call BackOne('123-456') 154 call assert_fails("BackOne('123-456')") 155 156 :" Delete three other characters and go back in time with g- 157 call feedkeys('$x', 'xt') 158 call feedkeys('x', 'xt') 159 call feedkeys('x', 'xt') 160 call assert_equal('123-', getline(1)) 161 call test_settime(101) 162 163 call BackOne('123-4') 164 call BackOne('123-45') 165 " skips '123-456' because it's older 166 call BackOne('-456') 167 call BackOne('3-456') 168 call BackOne('23-456') 169 call BackOne('123-456') 170 call assert_fails("BackOne('123-456')") 171 normal 10g+ 172 call assert_equal('123-', getline(1)) 173 174 :" Jump two seconds and go some seconds forward and backward 175 call test_settime(103) 176 call feedkeys("Aa\<Esc>", 'xt') 177 call feedkeys("Ab\<Esc>", 'xt') 178 call feedkeys("Ac\<Esc>", 'xt') 179 call assert_equal('123-abc', getline(1)) 180 earlier 1s 181 call assert_equal('123-', getline(1)) 182 earlier 3s 183 call assert_equal('123-456', getline(1)) 184 later 1s 185 call assert_equal('123-', getline(1)) 186 later 1h 187 call assert_equal('123-abc', getline(1)) 188 189 close! 190endfunc 191 192func Test_undolist() 193 new 194 set ul=100 195 196 let a = execute('undolist') 197 call assert_equal("\nNothing to undo", a) 198 199 " 1 leaf (2 changes). 200 call feedkeys('achange1', 'xt') 201 call feedkeys('achange2', 'xt') 202 let a = execute('undolist') 203 call assert_match("^\nnumber changes when *saved\n *2 *2 .*$", a) 204 205 " 2 leaves. 206 call feedkeys('u', 'xt') 207 call feedkeys('achange3\<Esc>', 'xt') 208 let a = execute('undolist') 209 call assert_match("^\nnumber changes when *saved\n *2 *2 *.*\n *3 *2 .*$", a) 210 close! 211endfunc 212 213func Test_U_command() 214 new 215 set ul=100 216 call feedkeys("achange1\<Esc>", 'xt') 217 call feedkeys("achange2\<Esc>", 'xt') 218 norm! U 219 call assert_equal('', getline(1)) 220 norm! U 221 call assert_equal('change1change2', getline(1)) 222 close! 223endfunc 224 225func Test_undojoin() 226 new 227 call feedkeys("Goaaaa\<Esc>", 'xt') 228 call feedkeys("obbbb\<Esc>", 'xt') 229 call assert_equal(['aaaa', 'bbbb'], getline(2, '$')) 230 call feedkeys("u", 'xt') 231 call assert_equal(['aaaa'], getline(2, '$')) 232 call feedkeys("obbbb\<Esc>", 'xt') 233 undojoin 234 " Note: next change must not be as if typed 235 call feedkeys("occcc\<Esc>", 'x') 236 call assert_equal(['aaaa', 'bbbb', 'cccc'], getline(2, '$')) 237 call feedkeys("u", 'xt') 238 call assert_equal(['aaaa'], getline(2, '$')) 239 bwipe! 240endfunc 241 242func Test_undojoin_redo() 243 new 244 call setline(1, ['first line', 'second line']) 245 call feedkeys("ixx\<Esc>", 'xt') 246 call feedkeys(":undojoin | redo\<CR>", 'xt') 247 call assert_equal('xxfirst line', getline(1)) 248 call assert_equal('second line', getline(2)) 249 bwipe! 250endfunc 251 252" undojoin not allowed after undo 253func Test_undojoin_after_undo() 254 new 255 call feedkeys("ixx\<Esc>u", 'xt') 256 call assert_fails(':undojoin', 'E790:') 257 bwipe! 258endfunc 259 260" undojoin is a noop when no change yet, or when 'undolevels' is negative 261func Test_undojoin_noop() 262 new 263 call feedkeys(":undojoin\<CR>", 'xt') 264 call assert_equal([''], getline(1, '$')) 265 setlocal undolevels=-1 266 call feedkeys("ixx\<Esc>u", 'xt') 267 call feedkeys(":undojoin\<CR>", 'xt') 268 call assert_equal(['xx'], getline(1, '$')) 269 bwipe! 270endfunc 271 272func Test_undo_write() 273 call delete('Xtest') 274 split Xtest 275 call feedkeys("ione one one\<Esc>", 'xt') 276 w! 277 call feedkeys("otwo\<Esc>", 'xt') 278 call feedkeys("otwo\<Esc>", 'xt') 279 w 280 call feedkeys("othree\<Esc>", 'xt') 281 call assert_equal(['one one one', 'two', 'two', 'three'], getline(1, '$')) 282 earlier 1f 283 call assert_equal(['one one one', 'two', 'two'], getline(1, '$')) 284 earlier 1f 285 call assert_equal(['one one one'], getline(1, '$')) 286 earlier 1f 287 call assert_equal([''], getline(1, '$')) 288 later 1f 289 call assert_equal(['one one one'], getline(1, '$')) 290 later 1f 291 call assert_equal(['one one one', 'two', 'two'], getline(1, '$')) 292 later 1f 293 call assert_equal(['one one one', 'two', 'two', 'three'], getline(1, '$')) 294 295 close! 296 call delete('Xtest') 297 bwipe! Xtest 298endfunc 299 300func Test_insert_expr() 301 new 302 " calling setline() triggers undo sync 303 call feedkeys("oa\<Esc>", 'xt') 304 call feedkeys("ob\<Esc>", 'xt') 305 set ul=100 306 call feedkeys("o1\<Esc>a2\<C-R>=setline('.','1234')\<CR>\<CR>\<Esc>", 'x') 307 call assert_equal(['a', 'b', '120', '34'], getline(2, '$')) 308 call feedkeys("u", 'x') 309 call assert_equal(['a', 'b', '12'], getline(2, '$')) 310 call feedkeys("u", 'x') 311 call assert_equal(['a', 'b'], getline(2, '$')) 312 313 call feedkeys("oc\<Esc>", 'xt') 314 set ul=100 315 call feedkeys("o1\<Esc>a2\<C-R>=setline('.','1234')\<CR>\<CR>\<Esc>", 'x') 316 call assert_equal(['a', 'b', 'c', '120', '34'], getline(2, '$')) 317 call feedkeys("u", 'x') 318 call assert_equal(['a', 'b', 'c', '12'], getline(2, '$')) 319 320 call feedkeys("od\<Esc>", 'xt') 321 set ul=100 322 call feedkeys("o1\<Esc>a2\<C-R>=string(123)\<CR>\<Esc>", 'x') 323 call assert_equal(['a', 'b', 'c', '12', 'd', '12123'], getline(2, '$')) 324 call feedkeys("u", 'x') 325 call assert_equal(['a', 'b', 'c', '12', 'd'], getline(2, '$')) 326 327 close! 328endfunc 329 330func Test_undofile_earlier() 331 " Issue #1254 332 " create undofile with timestamps older than Vim startup time. 333 let t0 = localtime() - 43200 334 call test_settime(t0) 335 new Xfile 336 call feedkeys("ione\<Esc>", 'xt') 337 set ul=100 338 call test_settime(t0 + 1) 339 call feedkeys("otwo\<Esc>", 'xt') 340 set ul=100 341 call test_settime(t0 + 2) 342 call feedkeys("othree\<Esc>", 'xt') 343 set ul=100 344 w 345 wundo Xundofile 346 bwipe! 347 " restore normal timestamps. 348 call test_settime(0) 349 new Xfile 350 rundo Xundofile 351 earlier 1d 352 call assert_equal('', getline(1)) 353 bwipe! 354 call delete('Xfile') 355 call delete('Xundofile') 356endfunc 357 358func Test_wundo_errors() 359 new 360 call setline(1, 'hello') 361 call assert_fails('wundo! Xdoesnotexist/Xundofile', 'E828:') 362 bwipe! 363endfunc 364 365" Check that reading a truncated undo file doesn't hang. 366func Test_undofile_truncated() 367 new 368 call setline(1, 'hello') 369 set ul=100 370 wundo Xundofile 371 let contents = readfile('Xundofile', 'B') 372 373 " try several sizes 374 for size in range(20, 500, 33) 375 call writefile(contents[0:size], 'Xundofile') 376 call assert_fails('rundo Xundofile', 'E825:') 377 endfor 378 379 bwipe! 380 call delete('Xundofile') 381endfunc 382 383func Test_rundo_errors() 384 call assert_fails('rundo XfileDoesNotExist', 'E822:') 385 386 call writefile(['abc'], 'Xundofile') 387 call assert_fails('rundo Xundofile', 'E823:') 388 389 call delete('Xundofile') 390endfunc 391 392" Test for undo working properly when executing commands from a register. 393" Also test this in an empty buffer. 394func Test_cmd_in_reg_undo() 395 enew! 396 let @a = "Ox\<Esc>jAy\<Esc>kdd" 397 edit +/^$ test_undo.vim 398 normal @au 399 call assert_equal(0, &modified) 400 return 401 new 402 normal @au 403 call assert_equal(0, &modified) 404 only! 405 let @a = '' 406endfunc 407 408" This used to cause an illegal memory access 409func Test_undo_append() 410 new 411 call feedkeys("axx\<Esc>v", 'xt') 412 undo 413 norm o 414 quit 415endfunc 416 417func Test_undo_0() 418 new 419 set ul=100 420 normal i1 421 undo 422 normal i2 423 undo 424 normal i3 425 426 undo 0 427 let d = undotree() 428 call assert_equal('', getline(1)) 429 call assert_equal(0, d.seq_cur) 430 431 redo 432 let d = undotree() 433 call assert_equal('3', getline(1)) 434 call assert_equal(3, d.seq_cur) 435 436 undo 2 437 undo 0 438 let d = undotree() 439 call assert_equal('', getline(1)) 440 call assert_equal(0, d.seq_cur) 441 442 redo 443 let d = undotree() 444 call assert_equal('2', getline(1)) 445 call assert_equal(2, d.seq_cur) 446 447 undo 1 448 undo 0 449 let d = undotree() 450 call assert_equal('', getline(1)) 451 call assert_equal(0, d.seq_cur) 452 453 redo 454 let d = undotree() 455 call assert_equal('1', getline(1)) 456 call assert_equal(1, d.seq_cur) 457 458 bwipe! 459endfunc 460 461" undo or redo are noop if there is nothing to undo or redo 462func Test_undo_redo_noop() 463 new 464 call assert_fails('undo 2', 'E830:') 465 466 message clear 467 undo 468 let messages = split(execute('message'), "\n") 469 call assert_equal('Already at oldest change', messages[-1]) 470 471 message clear 472 redo 473 let messages = split(execute('message'), "\n") 474 call assert_equal('Already at newest change', messages[-1]) 475 476 bwipe! 477endfunc 478 479func Test_redo_empty_line() 480 new 481 exe "norm\x16r\x160" 482 exe "norm." 483 bwipe! 484endfunc 485 486funct Test_undofile() 487 " Test undofile() without setting 'undodir'. 488 if has('persistent_undo') 489 call assert_equal(fnamemodify('.Xundofoo.un~', ':p'), undofile('Xundofoo')) 490 else 491 call assert_equal('', undofile('Xundofoo')) 492 endif 493 call assert_equal('', undofile('')) 494 495 " Test undofile() with 'undodir' set to to an existing directory. 496 call mkdir('Xundodir') 497 set undodir=Xundodir 498 let cwd = getcwd() 499 if has('win32') 500 " Replace windows drive such as C:... into C%... 501 let cwd = substitute(cwd, '^\([a-zA-Z]\):', '\1%', 'g') 502 endif 503 let cwd = substitute(cwd . '/Xundofoo', '/', '%', 'g') 504 if has('persistent_undo') 505 call assert_equal('Xundodir/' . cwd, undofile('Xundofoo')) 506 else 507 call assert_equal('', undofile('Xundofoo')) 508 endif 509 call assert_equal('', undofile('')) 510 call delete('Xundodir', 'd') 511 512 " Test undofile() with 'undodir' set to a non-existing directory. 513 call assert_equal('', 'Xundofoo'->undofile()) 514 515 if !has('win32') && isdirectory('/tmp') 516 set undodir=/tmp 517 if has('osx') 518 call assert_equal('/tmp/%private%tmp%file', undofile('///tmp/file')) 519 else 520 call assert_equal('/tmp/%tmp%file', undofile('///tmp/file')) 521 endif 522 endif 523 524 set undodir& 525endfunc 526 527" Tests for the undo file 528" Explicitly break changes up in undo-able pieces by setting 'undolevels'. 529func Test_undofile_2() 530 set undolevels=100 undofile 531 edit Xtestfile 532 call append(0, 'this is one line') 533 call cursor(1, 1) 534 535 " first a simple one-line change. 536 set undolevels=100 537 s/one/ONE/ 538 set undolevels=100 539 write 540 bwipe! 541 edit Xtestfile 542 undo 543 call assert_equal('this is one line', getline(1)) 544 545 " change in original file fails check 546 set noundofile 547 edit! Xtestfile 548 s/line/Line/ 549 write 550 set undofile 551 bwipe! 552 edit Xtestfile 553 undo 554 call assert_equal('this is ONE Line', getline(1)) 555 556 " add 10 lines, delete 6 lines, undo 3 557 set undofile 558 call setbufline(0, 1, ['one', 'two', 'three', 'four', 'five', 'six', 559 \ 'seven', 'eight', 'nine', 'ten']) 560 set undolevels=100 561 normal 3Gdd 562 set undolevels=100 563 normal dd 564 set undolevels=100 565 normal dd 566 set undolevels=100 567 normal dd 568 set undolevels=100 569 normal dd 570 set undolevels=100 571 normal dd 572 set undolevels=100 573 write 574 bwipe! 575 edit Xtestfile 576 normal uuu 577 call assert_equal(['one', 'two', 'six', 'seven', 'eight', 'nine', 'ten'], 578 \ getline(1, '$')) 579 580 " Test that reading the undofiles when setting undofile works 581 set noundofile undolevels=0 582 exe "normal i\n" 583 undo 584 edit! Xtestfile 585 set undofile undolevels=100 586 normal uuuuuu 587 call assert_equal(['one', 'two', 'three', 'four', 'five', 'six', 'seven', 588 \ 'eight', 'nine', 'ten'], getline(1, '$')) 589 590 bwipe! 591 call delete('Xtestfile') 592 let ufile = has('vms') ? '_un_Xtestfile' : '.Xtestfile.un~' 593 call delete(ufile) 594 set undofile& undolevels& 595endfunc 596 597" Test 'undofile' using a file encrypted with 'zip' crypt method 598func Test_undofile_cryptmethod_zip() 599 edit Xtestfile 600 set undofile cryptmethod=zip 601 call append(0, ['monday', 'tuesday', 'wednesday', 'thursday', 'friday']) 602 call cursor(5, 1) 603 604 set undolevels=100 605 normal kkkdd 606 set undolevels=100 607 normal dd 608 set undolevels=100 609 normal dd 610 set undolevels=100 611 " encrypt the file using key 'foobar' 612 call feedkeys("foobar\nfoobar\n") 613 X 614 write! 615 bwipe! 616 617 call feedkeys("foobar\n") 618 edit Xtestfile 619 set key= 620 normal uu 621 call assert_equal(['monday', 'wednesday', 'thursday', 'friday', ''], 622 \ getline(1, '$')) 623 624 bwipe! 625 call delete('Xtestfile') 626 let ufile = has('vms') ? '_un_Xtestfile' : '.Xtestfile.un~' 627 call delete(ufile) 628 set undofile& undolevels& cryptmethod& 629endfunc 630 631" Test 'undofile' using a file encrypted with 'blowfish' crypt method 632func Test_undofile_cryptmethod_blowfish() 633 edit Xtestfile 634 set undofile cryptmethod=blowfish 635 call append(0, ['jan', 'feb', 'mar', 'apr', 'jun']) 636 call cursor(5, 1) 637 638 set undolevels=100 639 exe 'normal kk0ifoo ' 640 set undolevels=100 641 normal dd 642 set undolevels=100 643 exe 'normal ibar ' 644 set undolevels=100 645 " encrypt the file using key 'foobar' 646 call feedkeys("foobar\nfoobar\n") 647 X 648 write! 649 bwipe! 650 651 call feedkeys("foobar\n") 652 edit Xtestfile 653 set key= 654 call search('bar') 655 call assert_equal('bar apr', getline('.')) 656 undo 657 call assert_equal('apr', getline('.')) 658 undo 659 call assert_equal('foo mar', getline('.')) 660 undo 661 call assert_equal('mar', getline('.')) 662 663 bwipe! 664 call delete('Xtestfile') 665 let ufile = has('vms') ? '_un_Xtestfile' : '.Xtestfile.un~' 666 call delete(ufile) 667 set undofile& undolevels& cryptmethod& 668endfunc 669 670" Test 'undofile' using a file encrypted with 'blowfish2' crypt method 671func Test_undofile_cryptmethod_blowfish2() 672 edit Xtestfile 673 set undofile cryptmethod=blowfish2 674 call append(0, ['jan', 'feb', 'mar', 'apr', 'jun']) 675 call cursor(5, 1) 676 677 set undolevels=100 678 exe 'normal kk0ifoo ' 679 set undolevels=100 680 normal dd 681 set undolevels=100 682 exe 'normal ibar ' 683 set undolevels=100 684 " encrypt the file using key 'foo2bar' 685 call feedkeys("foo2bar\nfoo2bar\n") 686 X 687 write! 688 bwipe! 689 690 call feedkeys("foo2bar\n") 691 edit Xtestfile 692 set key= 693 call search('bar') 694 call assert_equal('bar apr', getline('.')) 695 normal u 696 call assert_equal('apr', getline('.')) 697 normal u 698 call assert_equal('foo mar', getline('.')) 699 normal u 700 call assert_equal('mar', getline('.')) 701 702 bwipe! 703 call delete('Xtestfile') 704 let ufile = has('vms') ? '_un_Xtestfile' : '.Xtestfile.un~' 705 call delete(ufile) 706 set undofile& undolevels& cryptmethod& 707endfunc 708 709" vim: shiftwidth=2 sts=2 expandtab 710