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