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 252func Test_undo_write() 253 call delete('Xtest') 254 split Xtest 255 call feedkeys("ione one one\<Esc>", 'xt') 256 w! 257 call feedkeys("otwo\<Esc>", 'xt') 258 call feedkeys("otwo\<Esc>", 'xt') 259 w 260 call feedkeys("othree\<Esc>", 'xt') 261 call assert_equal(['one one one', 'two', 'two', 'three'], getline(1, '$')) 262 earlier 1f 263 call assert_equal(['one one one', 'two', 'two'], getline(1, '$')) 264 earlier 1f 265 call assert_equal(['one one one'], getline(1, '$')) 266 earlier 1f 267 call assert_equal([''], getline(1, '$')) 268 later 1f 269 call assert_equal(['one one one'], getline(1, '$')) 270 later 1f 271 call assert_equal(['one one one', 'two', 'two'], getline(1, '$')) 272 later 1f 273 call assert_equal(['one one one', 'two', 'two', 'three'], getline(1, '$')) 274 275 close! 276 call delete('Xtest') 277 bwipe! Xtest 278endfunc 279 280func Test_insert_expr() 281 new 282 " calling setline() triggers undo sync 283 call feedkeys("oa\<Esc>", 'xt') 284 call feedkeys("ob\<Esc>", 'xt') 285 set ul=100 286 call feedkeys("o1\<Esc>a2\<C-R>=setline('.','1234')\<CR>\<CR>\<Esc>", 'x') 287 call assert_equal(['a', 'b', '120', '34'], getline(2, '$')) 288 call feedkeys("u", 'x') 289 call assert_equal(['a', 'b', '12'], getline(2, '$')) 290 call feedkeys("u", 'x') 291 call assert_equal(['a', 'b'], getline(2, '$')) 292 293 call feedkeys("oc\<Esc>", 'xt') 294 set ul=100 295 call feedkeys("o1\<Esc>a2\<C-R>=setline('.','1234')\<CR>\<CR>\<Esc>", 'x') 296 call assert_equal(['a', 'b', 'c', '120', '34'], getline(2, '$')) 297 call feedkeys("u", 'x') 298 call assert_equal(['a', 'b', 'c', '12'], getline(2, '$')) 299 300 call feedkeys("od\<Esc>", 'xt') 301 set ul=100 302 call feedkeys("o1\<Esc>a2\<C-R>=string(123)\<CR>\<Esc>", 'x') 303 call assert_equal(['a', 'b', 'c', '12', 'd', '12123'], getline(2, '$')) 304 call feedkeys("u", 'x') 305 call assert_equal(['a', 'b', 'c', '12', 'd'], getline(2, '$')) 306 307 close! 308endfunc 309 310func Test_undofile_earlier() 311 " Issue #1254 312 " create undofile with timestamps older than Vim startup time. 313 let t0 = localtime() - 43200 314 call test_settime(t0) 315 new Xfile 316 call feedkeys("ione\<Esc>", 'xt') 317 set ul=100 318 call test_settime(t0 + 1) 319 call feedkeys("otwo\<Esc>", 'xt') 320 set ul=100 321 call test_settime(t0 + 2) 322 call feedkeys("othree\<Esc>", 'xt') 323 set ul=100 324 w 325 wundo Xundofile 326 bwipe! 327 " restore normal timestamps. 328 call test_settime(0) 329 new Xfile 330 rundo Xundofile 331 earlier 1d 332 call assert_equal('', getline(1)) 333 bwipe! 334 call delete('Xfile') 335 call delete('Xundofile') 336endfunc 337 338" Check that reading a truncted undo file doesn't hang. 339func Test_undofile_truncated() 340 new 341 call setline(1, 'hello') 342 set ul=100 343 wundo Xundofile 344 let contents = readfile('Xundofile', 'B') 345 346 " try several sizes 347 for size in range(20, 500, 33) 348 call writefile(contents[0:size], 'Xundofile') 349 call assert_fails('rundo Xundofile', 'E825:') 350 endfor 351 352 bwipe! 353 call delete('Xundofile') 354endfunc 355 356" Test for undo working properly when executing commands from a register. 357" Also test this in an empty buffer. 358func Test_cmd_in_reg_undo() 359 enew! 360 let @a = "Ox\<Esc>jAy\<Esc>kdd" 361 edit +/^$ test_undo.vim 362 normal @au 363 call assert_equal(0, &modified) 364 return 365 new 366 normal @au 367 call assert_equal(0, &modified) 368 only! 369 let @a = '' 370endfunc 371 372" This used to cause an illegal memory access 373func Test_undo_append() 374 new 375 call feedkeys("axx\<Esc>v", 'xt') 376 undo 377 norm o 378 quit 379endfunc 380 381func Test_undo_0() 382 new 383 set ul=100 384 normal i1 385 undo 386 normal i2 387 undo 388 normal i3 389 390 undo 0 391 let d = undotree() 392 call assert_equal('', getline(1)) 393 call assert_equal(0, d.seq_cur) 394 395 redo 396 let d = undotree() 397 call assert_equal('3', getline(1)) 398 call assert_equal(3, d.seq_cur) 399 400 undo 2 401 undo 0 402 let d = undotree() 403 call assert_equal('', getline(1)) 404 call assert_equal(0, d.seq_cur) 405 406 redo 407 let d = undotree() 408 call assert_equal('2', getline(1)) 409 call assert_equal(2, d.seq_cur) 410 411 undo 1 412 undo 0 413 let d = undotree() 414 call assert_equal('', getline(1)) 415 call assert_equal(0, d.seq_cur) 416 417 redo 418 let d = undotree() 419 call assert_equal('1', getline(1)) 420 call assert_equal(1, d.seq_cur) 421 422 bwipe! 423endfunc 424 425func Test_redo_empty_line() 426 new 427 exe "norm\x16r\x160" 428 exe "norm." 429 bwipe! 430endfunc 431 432funct Test_undofile() 433 " Test undofile() without setting 'undodir'. 434 if has('persistent_undo') 435 call assert_equal(fnamemodify('.Xundofoo.un~', ':p'), undofile('Xundofoo')) 436 else 437 call assert_equal('', undofile('Xundofoo')) 438 endif 439 call assert_equal('', undofile('')) 440 441 " Test undofile() with 'undodir' set to to an existing directory. 442 call mkdir('Xundodir') 443 set undodir=Xundodir 444 let cwd = getcwd() 445 if has('win32') 446 " Replace windows drive such as C:... into C%... 447 let cwd = substitute(cwd, '^\([a-zA-Z]\):', '\1%', 'g') 448 endif 449 let cwd = substitute(cwd . '/Xundofoo', '/', '%', 'g') 450 if has('persistent_undo') 451 call assert_equal('Xundodir/' . cwd, undofile('Xundofoo')) 452 else 453 call assert_equal('', undofile('Xundofoo')) 454 endif 455 call assert_equal('', undofile('')) 456 call delete('Xundodir', 'd') 457 458 " Test undofile() with 'undodir' set to a non-existing directory. 459 call assert_equal('', 'Xundofoo'->undofile()) 460 461 if isdirectory('/tmp') 462 set undodir=/tmp 463 if has('osx') 464 call assert_equal('/tmp/%private%tmp%file', undofile('///tmp/file')) 465 else 466 call assert_equal('/tmp/%tmp%file', undofile('///tmp/file')) 467 endif 468 endif 469 470 set undodir& 471endfunc 472