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 298 299 call assert_fails('earlier xyz', 'E475:') 300endfunc 301 302func Test_insert_expr() 303 new 304 " calling setline() triggers undo sync 305 call feedkeys("oa\<Esc>", 'xt') 306 call feedkeys("ob\<Esc>", 'xt') 307 set ul=100 308 call feedkeys("o1\<Esc>a2\<C-R>=setline('.','1234')\<CR>\<CR>\<Esc>", 'x') 309 call assert_equal(['a', 'b', '120', '34'], getline(2, '$')) 310 call feedkeys("u", 'x') 311 call assert_equal(['a', 'b', '12'], getline(2, '$')) 312 call feedkeys("u", 'x') 313 call assert_equal(['a', 'b'], getline(2, '$')) 314 315 call feedkeys("oc\<Esc>", 'xt') 316 set ul=100 317 call feedkeys("o1\<Esc>a2\<C-R>=setline('.','1234')\<CR>\<CR>\<Esc>", 'x') 318 call assert_equal(['a', 'b', 'c', '120', '34'], getline(2, '$')) 319 call feedkeys("u", 'x') 320 call assert_equal(['a', 'b', 'c', '12'], getline(2, '$')) 321 322 call feedkeys("od\<Esc>", 'xt') 323 set ul=100 324 call feedkeys("o1\<Esc>a2\<C-R>=string(123)\<CR>\<Esc>", 'x') 325 call assert_equal(['a', 'b', 'c', '12', 'd', '12123'], getline(2, '$')) 326 call feedkeys("u", 'x') 327 call assert_equal(['a', 'b', 'c', '12', 'd'], getline(2, '$')) 328 329 close! 330endfunc 331 332func Test_undofile_earlier() 333 " Issue #1254 334 " create undofile with timestamps older than Vim startup time. 335 let t0 = localtime() - 43200 336 call test_settime(t0) 337 new Xfile 338 call feedkeys("ione\<Esc>", 'xt') 339 set ul=100 340 call test_settime(t0 + 1) 341 call feedkeys("otwo\<Esc>", 'xt') 342 set ul=100 343 call test_settime(t0 + 2) 344 call feedkeys("othree\<Esc>", 'xt') 345 set ul=100 346 w 347 wundo Xundofile 348 bwipe! 349 " restore normal timestamps. 350 call test_settime(0) 351 new Xfile 352 rundo Xundofile 353 earlier 1d 354 call assert_equal('', getline(1)) 355 bwipe! 356 call delete('Xfile') 357 call delete('Xundofile') 358endfunc 359 360func Test_wundo_errors() 361 new 362 call setline(1, 'hello') 363 call assert_fails('wundo! Xdoesnotexist/Xundofile', 'E828:') 364 bwipe! 365endfunc 366 367" Check that reading a truncated undo file doesn't hang. 368func Test_undofile_truncated() 369 new 370 call setline(1, 'hello') 371 set ul=100 372 wundo Xundofile 373 let contents = readfile('Xundofile', 'B') 374 375 " try several sizes 376 for size in range(20, 500, 33) 377 call writefile(contents[0:size], 'Xundofile') 378 call assert_fails('rundo Xundofile', 'E825:') 379 endfor 380 381 bwipe! 382 call delete('Xundofile') 383endfunc 384 385func Test_rundo_errors() 386 call assert_fails('rundo XfileDoesNotExist', 'E822:') 387 388 call writefile(['abc'], 'Xundofile') 389 call assert_fails('rundo Xundofile', 'E823:') 390 391 call delete('Xundofile') 392endfunc 393 394" Test for undo working properly when executing commands from a register. 395" Also test this in an empty buffer. 396func Test_cmd_in_reg_undo() 397 enew! 398 let @a = "Ox\<Esc>jAy\<Esc>kdd" 399 edit +/^$ test_undo.vim 400 normal @au 401 call assert_equal(0, &modified) 402 return 403 new 404 normal @au 405 call assert_equal(0, &modified) 406 only! 407 let @a = '' 408endfunc 409 410" This used to cause an illegal memory access 411func Test_undo_append() 412 new 413 call feedkeys("axx\<Esc>v", 'xt') 414 undo 415 norm o 416 quit 417endfunc 418 419func Test_undo_0() 420 new 421 set ul=100 422 normal i1 423 undo 424 normal i2 425 undo 426 normal i3 427 428 undo 0 429 let d = undotree() 430 call assert_equal('', getline(1)) 431 call assert_equal(0, d.seq_cur) 432 433 redo 434 let d = undotree() 435 call assert_equal('3', getline(1)) 436 call assert_equal(3, d.seq_cur) 437 438 undo 2 439 undo 0 440 let d = undotree() 441 call assert_equal('', getline(1)) 442 call assert_equal(0, d.seq_cur) 443 444 redo 445 let d = undotree() 446 call assert_equal('2', getline(1)) 447 call assert_equal(2, d.seq_cur) 448 449 undo 1 450 undo 0 451 let d = undotree() 452 call assert_equal('', getline(1)) 453 call assert_equal(0, d.seq_cur) 454 455 redo 456 let d = undotree() 457 call assert_equal('1', getline(1)) 458 call assert_equal(1, d.seq_cur) 459 460 bwipe! 461endfunc 462 463" undo or redo are noop if there is nothing to undo or redo 464func Test_undo_redo_noop() 465 new 466 call assert_fails('undo 2', 'E830:') 467 468 message clear 469 undo 470 let messages = split(execute('message'), "\n") 471 call assert_equal('Already at oldest change', messages[-1]) 472 473 message clear 474 redo 475 let messages = split(execute('message'), "\n") 476 call assert_equal('Already at newest change', messages[-1]) 477 478 bwipe! 479endfunc 480 481func Test_redo_empty_line() 482 new 483 exe "norm\x16r\x160" 484 exe "norm." 485 bwipe! 486endfunc 487 488funct Test_undofile() 489 " Test undofile() without setting 'undodir'. 490 if has('persistent_undo') 491 call assert_equal(fnamemodify('.Xundofoo.un~', ':p'), undofile('Xundofoo')) 492 else 493 call assert_equal('', undofile('Xundofoo')) 494 endif 495 call assert_equal('', undofile('')) 496 497 " Test undofile() with 'undodir' set to to an existing directory. 498 call mkdir('Xundodir') 499 set undodir=Xundodir 500 let cwd = getcwd() 501 if has('win32') 502 " Replace windows drive such as C:... into C%... 503 let cwd = substitute(cwd, '^\([a-zA-Z]\):', '\1%', 'g') 504 endif 505 let cwd = substitute(cwd . '/Xundofoo', '/', '%', 'g') 506 if has('persistent_undo') 507 call assert_equal('Xundodir/' . cwd, undofile('Xundofoo')) 508 else 509 call assert_equal('', undofile('Xundofoo')) 510 endif 511 call assert_equal('', undofile('')) 512 call delete('Xundodir', 'd') 513 514 " Test undofile() with 'undodir' set to a non-existing directory. 515 call assert_equal('', 'Xundofoo'->undofile()) 516 517 if !has('win32') && isdirectory('/tmp') 518 set undodir=/tmp 519 if has('osx') 520 call assert_equal('/tmp/%private%tmp%file', undofile('///tmp/file')) 521 else 522 call assert_equal('/tmp/%tmp%file', undofile('///tmp/file')) 523 endif 524 endif 525 526 set undodir& 527endfunc 528 529" Tests for the undo file 530" Explicitly break changes up in undo-able pieces by setting 'undolevels'. 531func Test_undofile_2() 532 set undolevels=100 undofile 533 edit Xtestfile 534 call append(0, 'this is one line') 535 call cursor(1, 1) 536 537 " first a simple one-line change. 538 set undolevels=100 539 s/one/ONE/ 540 set undolevels=100 541 write 542 bwipe! 543 edit Xtestfile 544 undo 545 call assert_equal('this is one line', getline(1)) 546 547 " change in original file fails check 548 set noundofile 549 edit! Xtestfile 550 s/line/Line/ 551 write 552 set undofile 553 bwipe! 554 edit Xtestfile 555 undo 556 call assert_equal('this is ONE Line', getline(1)) 557 558 " add 10 lines, delete 6 lines, undo 3 559 set undofile 560 call setbufline(0, 1, ['one', 'two', 'three', 'four', 'five', 'six', 561 \ 'seven', 'eight', 'nine', 'ten']) 562 set undolevels=100 563 normal 3Gdd 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 normal dd 574 set undolevels=100 575 write 576 bwipe! 577 edit Xtestfile 578 normal uuu 579 call assert_equal(['one', 'two', 'six', 'seven', 'eight', 'nine', 'ten'], 580 \ getline(1, '$')) 581 582 " Test that reading the undofiles when setting undofile works 583 set noundofile undolevels=0 584 exe "normal i\n" 585 undo 586 edit! Xtestfile 587 set undofile undolevels=100 588 normal uuuuuu 589 call assert_equal(['one', 'two', 'three', 'four', 'five', 'six', 'seven', 590 \ 'eight', 'nine', 'ten'], getline(1, '$')) 591 592 bwipe! 593 call delete('Xtestfile') 594 let ufile = has('vms') ? '_un_Xtestfile' : '.Xtestfile.un~' 595 call delete(ufile) 596 set undofile& undolevels& 597endfunc 598 599" Test 'undofile' using a file encrypted with 'zip' crypt method 600func Test_undofile_cryptmethod_zip() 601 edit Xtestfile 602 set undofile cryptmethod=zip 603 call append(0, ['monday', 'tuesday', 'wednesday', 'thursday', 'friday']) 604 call cursor(5, 1) 605 606 set undolevels=100 607 normal kkkdd 608 set undolevels=100 609 normal dd 610 set undolevels=100 611 normal dd 612 set undolevels=100 613 " encrypt the file using key 'foobar' 614 call feedkeys("foobar\nfoobar\n") 615 X 616 write! 617 bwipe! 618 619 call feedkeys("foobar\n") 620 edit Xtestfile 621 set key= 622 normal uu 623 call assert_equal(['monday', 'wednesday', 'thursday', 'friday', ''], 624 \ getline(1, '$')) 625 626 bwipe! 627 call delete('Xtestfile') 628 let ufile = has('vms') ? '_un_Xtestfile' : '.Xtestfile.un~' 629 call delete(ufile) 630 set undofile& undolevels& cryptmethod& 631endfunc 632 633" Test 'undofile' using a file encrypted with 'blowfish' crypt method 634func Test_undofile_cryptmethod_blowfish() 635 edit Xtestfile 636 set undofile cryptmethod=blowfish 637 call append(0, ['jan', 'feb', 'mar', 'apr', 'jun']) 638 call cursor(5, 1) 639 640 set undolevels=100 641 exe 'normal kk0ifoo ' 642 set undolevels=100 643 normal dd 644 set undolevels=100 645 exe 'normal ibar ' 646 set undolevels=100 647 " encrypt the file using key 'foobar' 648 call feedkeys("foobar\nfoobar\n") 649 X 650 write! 651 bwipe! 652 653 call feedkeys("foobar\n") 654 edit Xtestfile 655 set key= 656 call search('bar') 657 call assert_equal('bar apr', getline('.')) 658 undo 659 call assert_equal('apr', getline('.')) 660 undo 661 call assert_equal('foo mar', getline('.')) 662 undo 663 call assert_equal('mar', getline('.')) 664 665 bwipe! 666 call delete('Xtestfile') 667 let ufile = has('vms') ? '_un_Xtestfile' : '.Xtestfile.un~' 668 call delete(ufile) 669 set undofile& undolevels& cryptmethod& 670endfunc 671 672" Test 'undofile' using a file encrypted with 'blowfish2' crypt method 673func Test_undofile_cryptmethod_blowfish2() 674 edit Xtestfile 675 set undofile cryptmethod=blowfish2 676 call append(0, ['jan', 'feb', 'mar', 'apr', 'jun']) 677 call cursor(5, 1) 678 679 set undolevels=100 680 exe 'normal kk0ifoo ' 681 set undolevels=100 682 normal dd 683 set undolevels=100 684 exe 'normal ibar ' 685 set undolevels=100 686 " encrypt the file using key 'foo2bar' 687 call feedkeys("foo2bar\nfoo2bar\n") 688 X 689 write! 690 bwipe! 691 692 call feedkeys("foo2bar\n") 693 edit Xtestfile 694 set key= 695 call search('bar') 696 call assert_equal('bar apr', getline('.')) 697 normal u 698 call assert_equal('apr', getline('.')) 699 normal u 700 call assert_equal('foo mar', getline('.')) 701 normal u 702 call assert_equal('mar', getline('.')) 703 704 bwipe! 705 call delete('Xtestfile') 706 let ufile = has('vms') ? '_un_Xtestfile' : '.Xtestfile.un~' 707 call delete(ufile) 708 set undofile& undolevels& cryptmethod& 709endfunc 710 711" vim: shiftwidth=2 sts=2 expandtab 712