xref: /vim-8.2.3635/src/testdir/test_undo.vim (revision 98be7fec)
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