neovim/test/old/testdir/test_undo.vim
dundargoc af23d17388
test: move oldtests to test directory (#22536)
The new oldtest directory is in test/old/testdir. The reason for this is
that many tests have hardcoded the parent directory name to be
'testdir'.
2023-03-07 11:13:04 +08:00

806 lines
20 KiB
VimL

" Tests for the undo tree.
" Since this script is sourced we need to explicitly break changes up in
" undo-able pieces. Do that by setting 'undolevels'.
" Also tests :earlier and :later.
source check.vim
source screendump.vim
func Test_undotree()
new
normal! Aabc
set ul=100
let d = undotree()
call assert_equal(1, d.seq_last)
call assert_equal(1, d.seq_cur)
call assert_equal(0, d.save_last)
call assert_equal(0, d.save_cur)
call assert_equal(1, len(d.entries))
call assert_equal(1, d.entries[0].newhead)
call assert_equal(1, d.entries[0].seq)
call assert_true(d.entries[0].time <= d.time_cur)
normal! Adef
set ul=100
let d = undotree()
call assert_equal(2, d.seq_last)
call assert_equal(2, d.seq_cur)
call assert_equal(0, d.save_last)
call assert_equal(0, d.save_cur)
call assert_equal(2, len(d.entries))
call assert_equal(1, d.entries[0].seq)
call assert_equal(1, d.entries[1].newhead)
call assert_equal(2, d.entries[1].seq)
call assert_true(d.entries[1].time <= d.time_cur)
undo
set ul=100
let d = undotree()
call assert_equal(2, d.seq_last)
call assert_equal(1, d.seq_cur)
call assert_equal(0, d.save_last)
call assert_equal(0, d.save_cur)
call assert_equal(2, len(d.entries))
call assert_equal(1, d.entries[0].seq)
call assert_equal(1, d.entries[1].curhead)
call assert_equal(1, d.entries[1].newhead)
call assert_equal(2, d.entries[1].seq)
call assert_true(d.entries[1].time == d.time_cur)
normal! Aghi
set ul=100
let d = undotree()
call assert_equal(3, d.seq_last)
call assert_equal(3, d.seq_cur)
call assert_equal(0, d.save_last)
call assert_equal(0, d.save_cur)
call assert_equal(2, len(d.entries))
call assert_equal(1, d.entries[0].seq)
call assert_equal(2, d.entries[1].alt[0].seq)
call assert_equal(1, d.entries[1].newhead)
call assert_equal(3, d.entries[1].seq)
call assert_true(d.entries[1].time <= d.time_cur)
undo
set ul=100
let d = undotree()
call assert_equal(3, d.seq_last)
call assert_equal(1, d.seq_cur)
call assert_equal(0, d.save_last)
call assert_equal(0, d.save_cur)
call assert_equal(2, len(d.entries))
call assert_equal(1, d.entries[0].seq)
call assert_equal(2, d.entries[1].alt[0].seq)
call assert_equal(1, d.entries[1].curhead)
call assert_equal(1, d.entries[1].newhead)
call assert_equal(3, d.entries[1].seq)
call assert_true(d.entries[1].time == d.time_cur)
w! Xtest
let d = undotree()
call assert_equal(1, d.save_cur)
call assert_equal(1, d.save_last)
call delete('Xtest')
bwipe! Xtest
endfunc
func FillBuffer()
for i in range(1,13)
put=i
" Set 'undolevels' to split undo.
exe "setg ul=" . &g:ul
endfor
endfunc
func Test_global_local_undolevels()
new one
set undolevels=5
call FillBuffer()
" will only undo the last 5 changes, end up with 13 - (5 + 1) = 7 lines
earlier 10
call assert_equal(5, &g:undolevels)
call assert_equal(-123456, &l:undolevels)
call assert_equal('7', getline('$'))
new two
setlocal undolevels=2
call FillBuffer()
" will only undo the last 2 changes, end up with 13 - (2 + 1) = 10 lines
earlier 10
call assert_equal(5, &g:undolevels)
call assert_equal(2, &l:undolevels)
call assert_equal('10', getline('$'))
setlocal ul=10
call assert_equal(5, &g:undolevels)
call assert_equal(10, &l:undolevels)
" Setting local value in "two" must not change local value in "one"
wincmd p
call assert_equal(5, &g:undolevels)
call assert_equal(-123456, &l:undolevels)
new three
setglobal ul=50
call assert_equal(50, &g:undolevels)
call assert_equal(-123456, &l:undolevels)
" Drop created windows
set ul&
new
only!
endfunc
func BackOne(expected)
call feedkeys('g-', 'xt')
call assert_equal(a:expected, getline(1))
endfunc
func Test_undo_del_chars()
throw 'Skipped: Nvim does not support test_settime()'
" Setup a buffer without creating undo entries
new
set ul=-1
call setline(1, ['123-456'])
set ul=100
1
call test_settime(100)
" Delete three characters and undo with g-
call feedkeys('x', 'xt')
call feedkeys('x', 'xt')
call feedkeys('x', 'xt')
call assert_equal('-456', getline(1))
call BackOne('3-456')
call BackOne('23-456')
call BackOne('123-456')
call assert_fails("BackOne('123-456')")
:" Delete three other characters and go back in time with g-
call feedkeys('$x', 'xt')
call feedkeys('x', 'xt')
call feedkeys('x', 'xt')
call assert_equal('123-', getline(1))
call test_settime(101)
call BackOne('123-4')
call BackOne('123-45')
" skips '123-456' because it's older
call BackOne('-456')
call BackOne('3-456')
call BackOne('23-456')
call BackOne('123-456')
call assert_fails("BackOne('123-456')")
normal 10g+
call assert_equal('123-', getline(1))
:" Jump two seconds and go some seconds forward and backward
call test_settime(103)
call feedkeys("Aa\<Esc>", 'xt')
call feedkeys("Ab\<Esc>", 'xt')
call feedkeys("Ac\<Esc>", 'xt')
call assert_equal('123-abc', getline(1))
earlier 1s
call assert_equal('123-', getline(1))
earlier 3s
call assert_equal('123-456', getline(1))
later 1s
call assert_equal('123-', getline(1))
later 1h
call assert_equal('123-abc', getline(1))
close!
endfunc
func Test_undolist()
new
set ul=100
let a = execute('undolist')
call assert_equal("\nNothing to undo", a)
" 1 leaf (2 changes).
call feedkeys('achange1', 'xt')
call feedkeys('achange2', 'xt')
let a = execute('undolist')
call assert_match("^\nnumber changes when *saved\n *2 *2 .*$", a)
" 2 leaves.
call feedkeys('u', 'xt')
call feedkeys('achange3\<Esc>', 'xt')
let a = execute('undolist')
call assert_match("^\nnumber changes when *saved\n *2 *2 *.*\n *3 *2 .*$", a)
close!
endfunc
func Test_U_command()
new
set ul=100
call feedkeys("achange1\<Esc>", 'xt')
call feedkeys("achange2\<Esc>", 'xt')
norm! U
call assert_equal('', getline(1))
norm! U
call assert_equal('change1change2', getline(1))
close!
endfunc
func Test_undojoin()
new
call feedkeys("Goaaaa\<Esc>", 'xt')
call feedkeys("obbbb\<Esc>", 'xt')
call assert_equal(['aaaa', 'bbbb'], getline(2, '$'))
call feedkeys("u", 'xt')
call assert_equal(['aaaa'], getline(2, '$'))
call feedkeys("obbbb\<Esc>", 'xt')
undojoin
" Note: next change must not be as if typed
call feedkeys("occcc\<Esc>", 'x')
call assert_equal(['aaaa', 'bbbb', 'cccc'], getline(2, '$'))
call feedkeys("u", 'xt')
call assert_equal(['aaaa'], getline(2, '$'))
bwipe!
endfunc
func Test_undojoin_redo()
new
call setline(1, ['first line', 'second line'])
call feedkeys("ixx\<Esc>", 'xt')
call feedkeys(":undojoin | redo\<CR>", 'xt')
call assert_equal('xxfirst line', getline(1))
call assert_equal('second line', getline(2))
bwipe!
endfunc
" undojoin not allowed after undo
func Test_undojoin_after_undo()
new
call feedkeys("ixx\<Esc>u", 'xt')
call assert_fails(':undojoin', 'E790:')
bwipe!
endfunc
" undojoin is a noop when no change yet, or when 'undolevels' is negative
func Test_undojoin_noop()
new
call feedkeys(":undojoin\<CR>", 'xt')
call assert_equal([''], getline(1, '$'))
setlocal undolevels=-1
call feedkeys("ixx\<Esc>u", 'xt')
call feedkeys(":undojoin\<CR>", 'xt')
call assert_equal(['xx'], getline(1, '$'))
bwipe!
endfunc
func Test_undo_write()
call delete('Xtest')
split Xtest
call feedkeys("ione one one\<Esc>", 'xt')
w!
call feedkeys("otwo\<Esc>", 'xt')
call feedkeys("otwo\<Esc>", 'xt')
w
call feedkeys("othree\<Esc>", 'xt')
call assert_equal(['one one one', 'two', 'two', 'three'], getline(1, '$'))
earlier 1f
call assert_equal(['one one one', 'two', 'two'], getline(1, '$'))
earlier 1f
call assert_equal(['one one one'], getline(1, '$'))
earlier 1f
call assert_equal([''], getline(1, '$'))
later 1f
call assert_equal(['one one one'], getline(1, '$'))
later 1f
call assert_equal(['one one one', 'two', 'two'], getline(1, '$'))
later 1f
call assert_equal(['one one one', 'two', 'two', 'three'], getline(1, '$'))
close!
call delete('Xtest')
bwipe! Xtest
call assert_fails('earlier xyz', 'E475:')
endfunc
func Test_insert_expr()
new
" calling setline() triggers undo sync
call feedkeys("oa\<Esc>", 'xt')
call feedkeys("ob\<Esc>", 'xt')
set ul=100
call feedkeys("o1\<Esc>a2\<C-R>=setline('.','1234')\<CR>\<CR>\<Esc>", 'x')
call assert_equal(['a', 'b', '120', '34'], getline(2, '$'))
call feedkeys("u", 'x')
call assert_equal(['a', 'b', '12'], getline(2, '$'))
call feedkeys("u", 'x')
call assert_equal(['a', 'b'], getline(2, '$'))
call feedkeys("oc\<Esc>", 'xt')
set ul=100
call feedkeys("o1\<Esc>a2\<C-R>=setline('.','1234')\<CR>\<CR>\<Esc>", 'x')
call assert_equal(['a', 'b', 'c', '120', '34'], getline(2, '$'))
call feedkeys("u", 'x')
call assert_equal(['a', 'b', 'c', '12'], getline(2, '$'))
call feedkeys("od\<Esc>", 'xt')
set ul=100
call feedkeys("o1\<Esc>a2\<C-R>=string(123)\<CR>\<Esc>", 'x')
call assert_equal(['a', 'b', 'c', '12', 'd', '12123'], getline(2, '$'))
call feedkeys("u", 'x')
call assert_equal(['a', 'b', 'c', '12', 'd'], getline(2, '$'))
close!
endfunc
func Test_undofile_earlier()
throw 'Skipped: Nvim does not support test_settime()'
" Issue #1254
" create undofile with timestamps older than Vim startup time.
let t0 = localtime() - 43200
call test_settime(t0)
new XfileEarlier
call feedkeys("ione\<Esc>", 'xt')
set ul=100
call test_settime(t0 + 1)
call feedkeys("otwo\<Esc>", 'xt')
set ul=100
call test_settime(t0 + 2)
call feedkeys("othree\<Esc>", 'xt')
set ul=100
w
wundo Xundofile
bwipe!
" restore normal timestamps.
call test_settime(0)
new XfileEarlier
rundo Xundofile
earlier 1d
call assert_equal('', getline(1))
bwipe!
call delete('XfileEarlier')
call delete('Xundofile')
endfunc
func Test_wundo_errors()
new
call setline(1, 'hello')
call assert_fails('wundo! Xdoesnotexist/Xundofile', 'E828:')
bwipe!
endfunc
" Check that reading a truncated undo file doesn't hang.
func Test_undofile_truncated()
new
call setline(1, 'hello')
set ul=100
wundo Xundofile
let contents = readfile('Xundofile', 'B')
" try several sizes
for size in range(20, 500, 33)
call writefile(contents[0:size], 'Xundofile')
call assert_fails('rundo Xundofile', 'E825:')
endfor
bwipe!
call delete('Xundofile')
endfunc
func Test_rundo_errors()
call assert_fails('rundo XfileDoesNotExist', 'E822:')
call writefile(['abc'], 'Xundofile')
call assert_fails('rundo Xundofile', 'E823:')
call delete('Xundofile')
endfunc
func Test_undofile_next()
set undofile
new Xfoo.txt
execute "norm ix\<c-g>uy\<c-g>uz\<Esc>"
write
bwipe
next Xfoo.txt
call assert_equal('xyz', getline(1))
silent undo
call assert_equal('xy', getline(1))
silent undo
call assert_equal('x', getline(1))
bwipe!
call delete('Xfoo.txt')
call delete('.Xfoo.txt.un~')
set undofile&
endfunc
" Test for undo working properly when executing commands from a register.
" Also test this in an empty buffer.
func Test_cmd_in_reg_undo()
enew!
let @a = "Ox\<Esc>jAy\<Esc>kdd"
edit +/^$ test_undo.vim
normal @au
call assert_equal(0, &modified)
return
new
normal @au
call assert_equal(0, &modified)
only!
let @a = ''
endfunc
" This used to cause an illegal memory access
func Test_undo_append()
new
call feedkeys("axx\<Esc>v", 'xt')
undo
norm o
quit
endfunc
func Test_undo_0()
new
set ul=100
normal i1
undo
normal i2
undo
normal i3
undo 0
let d = undotree()
call assert_equal('', getline(1))
call assert_equal(0, d.seq_cur)
redo
let d = undotree()
call assert_equal('3', getline(1))
call assert_equal(3, d.seq_cur)
undo 2
undo 0
let d = undotree()
call assert_equal('', getline(1))
call assert_equal(0, d.seq_cur)
redo
let d = undotree()
call assert_equal('2', getline(1))
call assert_equal(2, d.seq_cur)
undo 1
undo 0
let d = undotree()
call assert_equal('', getline(1))
call assert_equal(0, d.seq_cur)
redo
let d = undotree()
call assert_equal('1', getline(1))
call assert_equal(1, d.seq_cur)
bwipe!
endfunc
" undo or redo are noop if there is nothing to undo or redo
func Test_undo_redo_noop()
new
call assert_fails('undo 2', 'E830:')
message clear
undo
let messages = split(execute('message'), "\n")
call assert_equal('Already at oldest change', messages[-1])
message clear
redo
let messages = split(execute('message'), "\n")
call assert_equal('Already at newest change', messages[-1])
bwipe!
endfunc
func Test_redo_empty_line()
new
exe "norm\x16r\x160"
exe "norm."
bwipe!
endfunc
funct Test_undofile()
" Test undofile() without setting 'undodir'.
if has('persistent_undo')
call assert_equal(fnamemodify('.Xundofoo.un~', ':p'), undofile('Xundofoo'))
else
call assert_equal('', undofile('Xundofoo'))
endif
call assert_equal('', undofile(''))
" Test undofile() with 'undodir' set to to an existing directory.
call mkdir('Xundodir')
set undodir=Xundodir
let cwd = getcwd()
if has('win32')
" Replace windows drive such as C:... into C%...
let cwd = substitute(cwd, '^\([a-zA-Z]\):', '\1%', 'g')
endif
let cwd = substitute(cwd . '/Xundofoo', '/', '%', 'g')
if has('persistent_undo')
call assert_equal('Xundodir/' . cwd, undofile('Xundofoo'))
else
call assert_equal('', undofile('Xundofoo'))
endif
call assert_equal('', undofile(''))
call delete('Xundodir', 'd')
" Test undofile() with 'undodir' set to a non-existing directory.
" call assert_equal('', 'Xundofoo'->undofile())
if isdirectory('/tmp')
set undodir=/tmp
if has('osx')
call assert_equal('/tmp/%private%tmp%file', undofile('///tmp/file'))
else
call assert_equal('/tmp/%tmp%file', undofile('///tmp/file'))
endif
endif
set undodir&
endfunc
" Tests for the undo file
" Explicitly break changes up in undo-able pieces by setting 'undolevels'.
func Test_undofile_2()
set undolevels=100 undofile
edit Xtestfile
call append(0, 'this is one line')
call cursor(1, 1)
" first a simple one-line change.
set undolevels=100
s/one/ONE/
set undolevels=100
write
bwipe!
edit Xtestfile
undo
call assert_equal('this is one line', getline(1))
" change in original file fails check
set noundofile
edit! Xtestfile
s/line/Line/
write
set undofile
bwipe!
edit Xtestfile
undo
call assert_equal('this is ONE Line', getline(1))
" add 10 lines, delete 6 lines, undo 3
set undofile
call setbufline('%', 1, ['one', 'two', 'three', 'four', 'five', 'six',
\ 'seven', 'eight', 'nine', 'ten'])
set undolevels=100
normal 3Gdd
set undolevels=100
normal dd
set undolevels=100
normal dd
set undolevels=100
normal dd
set undolevels=100
normal dd
set undolevels=100
normal dd
set undolevels=100
write
bwipe!
edit Xtestfile
normal uuu
call assert_equal(['one', 'two', 'six', 'seven', 'eight', 'nine', 'ten'],
\ getline(1, '$'))
" Test that reading the undofiles when setting undofile works
set noundofile undolevels=0
exe "normal i\n"
undo
edit! Xtestfile
set undofile undolevels=100
normal uuuuuu
call assert_equal(['one', 'two', 'three', 'four', 'five', 'six', 'seven',
\ 'eight', 'nine', 'ten'], getline(1, '$'))
bwipe!
call delete('Xtestfile')
let ufile = has('vms') ? '_un_Xtestfile' : '.Xtestfile.un~'
call delete(ufile)
set undofile& undolevels&
endfunc
" Test 'undofile' using a file encrypted with 'zip' crypt method
func Test_undofile_cryptmethod_zip()
throw 'skipped: Nvim does not support cryptmethod'
edit Xtestfile
set undofile cryptmethod=zip
call append(0, ['monday', 'tuesday', 'wednesday', 'thursday', 'friday'])
call cursor(5, 1)
set undolevels=100
normal kkkdd
set undolevels=100
normal dd
set undolevels=100
normal dd
set undolevels=100
" encrypt the file using key 'foobar'
call feedkeys("foobar\nfoobar\n")
X
write!
bwipe!
call feedkeys("foobar\n")
edit Xtestfile
set key=
normal uu
call assert_equal(['monday', 'wednesday', 'thursday', 'friday', ''],
\ getline(1, '$'))
bwipe!
call delete('Xtestfile')
let ufile = has('vms') ? '_un_Xtestfile' : '.Xtestfile.un~'
call delete(ufile)
set undofile& undolevels& cryptmethod&
endfunc
" Test 'undofile' using a file encrypted with 'blowfish' crypt method
func Test_undofile_cryptmethod_blowfish()
throw 'skipped: Nvim does not support cryptmethod'
edit Xtestfile
set undofile cryptmethod=blowfish
call append(0, ['jan', 'feb', 'mar', 'apr', 'jun'])
call cursor(5, 1)
set undolevels=100
exe 'normal kk0ifoo '
set undolevels=100
normal dd
set undolevels=100
exe 'normal ibar '
set undolevels=100
" encrypt the file using key 'foobar'
call feedkeys("foobar\nfoobar\n")
X
write!
bwipe!
call feedkeys("foobar\n")
edit Xtestfile
set key=
call search('bar')
call assert_equal('bar apr', getline('.'))
undo
call assert_equal('apr', getline('.'))
undo
call assert_equal('foo mar', getline('.'))
undo
call assert_equal('mar', getline('.'))
bwipe!
call delete('Xtestfile')
let ufile = has('vms') ? '_un_Xtestfile' : '.Xtestfile.un~'
call delete(ufile)
set undofile& undolevels& cryptmethod&
endfunc
" Test 'undofile' using a file encrypted with 'blowfish2' crypt method
func Test_undofile_cryptmethod_blowfish2()
throw 'skipped: Nvim does not support cryptmethod'
edit Xtestfile
set undofile cryptmethod=blowfish2
call append(0, ['jan', 'feb', 'mar', 'apr', 'jun'])
call cursor(5, 1)
set undolevels=100
exe 'normal kk0ifoo '
set undolevels=100
normal dd
set undolevels=100
exe 'normal ibar '
set undolevels=100
" encrypt the file using key 'foo2bar'
call feedkeys("foo2bar\nfoo2bar\n")
X
write!
bwipe!
call feedkeys("foo2bar\n")
edit Xtestfile
set key=
call search('bar')
call assert_equal('bar apr', getline('.'))
normal u
call assert_equal('apr', getline('.'))
normal u
call assert_equal('foo mar', getline('.'))
normal u
call assert_equal('mar', getline('.'))
bwipe!
call delete('Xtestfile')
let ufile = has('vms') ? '_un_Xtestfile' : '.Xtestfile.un~'
call delete(ufile)
set undofile& undolevels& cryptmethod&
endfunc
" Test for redoing with incrementing numbered registers
func Test_redo_repeat_numbered_register()
new
for [i, v] in [[1, 'one'], [2, 'two'], [3, 'three'],
\ [4, 'four'], [5, 'five'], [6, 'six'],
\ [7, 'seven'], [8, 'eight'], [9, 'nine']]
exe 'let @' .. i .. '="' .. v .. '\n"'
endfor
call feedkeys('"1p.........', 'xt')
call assert_equal(['', 'one', 'two', 'three', 'four', 'five', 'six',
\ 'seven', 'eight', 'nine', 'nine'], getline(1, '$'))
bwipe!
endfunc
" Test for redo in insert mode using CTRL-O with multibyte characters
func Test_redo_multibyte_in_insert_mode()
new
call feedkeys("a\<C-K>ft", 'xt')
call feedkeys("uiHe\<C-O>.llo", 'xt')
call assert_equal("He\ufb05llo", getline(1))
bwipe!
endfunc
func Test_undo_mark()
new
" The undo is applied to the only line.
call setline(1, 'hello')
call feedkeys("ggyiw$p", 'xt')
undo
call assert_equal([0, 1, 1, 0], getpos("'["))
call assert_equal([0, 1, 1, 0], getpos("']"))
" The undo removes the last line.
call feedkeys("Goaaaa\<Esc>", 'xt')
call feedkeys("obbbb\<Esc>", 'xt')
undo
call assert_equal([0, 2, 1, 0], getpos("'["))
call assert_equal([0, 2, 1, 0], getpos("']"))
bwipe!
endfunc
func Test_undo_after_write()
" use a terminal to make undo work like when text is typed
CheckRunVimInTerminal
let lines =<< trim END
edit Xtestfile.txt
set undolevels=100 undofile
imap . <Cmd>write<CR>
write
END
call writefile(lines, 'Xtest_undo_after_write', 'D')
let buf = RunVimInTerminal('-S Xtest_undo_after_write', #{rows: 6})
call term_sendkeys(buf, "Otest.\<CR>boo!!!\<Esc>")
sleep 100m
call term_sendkeys(buf, "u")
call VerifyScreenDump(buf, 'Test_undo_after_write_1', {})
call term_sendkeys(buf, "u")
call VerifyScreenDump(buf, 'Test_undo_after_write_2', {})
call StopVimInTerminal(buf)
call delete('Xtestfile.txt')
endfunc
" vim: shiftwidth=2 sts=2 expandtab