diff options
Diffstat (limited to '')
-rw-r--r-- | src/testdir/test_listener.vim | 453 |
1 files changed, 453 insertions, 0 deletions
diff --git a/src/testdir/test_listener.vim b/src/testdir/test_listener.vim new file mode 100644 index 0000000..413275d --- /dev/null +++ b/src/testdir/test_listener.vim @@ -0,0 +1,453 @@ +" tests for listener_add() and listener_remove() + +func s:StoreList(s, e, a, l) + let s:start = a:s + let s:end = a:e + let s:added = a:a + let s:text = getline(a:s) + let s:list = a:l +endfunc + +func s:AnotherStoreList(l) + let s:list2 = a:l +endfunc + +func s:EvilStoreList(l) + let s:list3 = a:l + call assert_fails("call add(a:l, 'myitem')", "E742:") +endfunc + +func Test_listening() + new + call setline(1, ['one', 'two']) + let s:list = [] + let id = listener_add({b, s, e, a, l -> s:StoreList(s, e, a, l)}) + call setline(1, 'one one') + call listener_flush() + call assert_equal([{'lnum': 1, 'end': 2, 'col': 1, 'added': 0}], s:list) + + " Undo is also a change + set undolevels& " start new undo block + call append(2, 'two two') + undo + call assert_equal([{'lnum': 3, 'end': 3, 'col': 1, 'added': 1}], s:list) + redraw + " the two changes are not merged + call assert_equal([{'lnum': 3, 'end': 4, 'col': 1, 'added': -1}], s:list) + 1 + + " Two listeners, both get called. Also check column. + call setline(1, ['one one', 'two']) + call listener_flush() + let id2 = listener_add({b, s, e, a, l -> s:AnotherStoreList(l)}) + let s:list = [] + let s:list2 = [] + exe "normal $asome\<Esc>" + redraw + call assert_equal([{'lnum': 1, 'end': 2, 'col': 8, 'added': 0}], s:list) + call assert_equal([{'lnum': 1, 'end': 2, 'col': 8, 'added': 0}], s:list2) + + " removing listener works + call listener_remove(id2) + call setline(1, ['one one', 'two']) + call listener_flush() + let s:list = [] + let s:list2 = [] + call setline(3, 'three') + redraw + call assert_equal([{'lnum': 3, 'end': 3, 'col': 1, 'added': 1}], s:list) + call assert_equal([], s:list2) + + " a change above a previous change without a line number change is reported + " together + call setline(1, ['one one', 'two']) + call listener_flush(bufnr()) + call append(2, 'two two') + call setline(1, 'something') + call bufnr()->listener_flush() + call assert_equal([{'lnum': 3, 'end': 3, 'col': 1, 'added': 1}, + \ {'lnum': 1, 'end': 2, 'col': 1, 'added': 0}], s:list) + call assert_equal(1, s:start) + call assert_equal(3, s:end) + call assert_equal(1, s:added) + + " an insert just above a previous change that was the last one does not get + " merged + call setline(1, ['one one', 'two']) + call listener_flush() + let s:list = [] + call setline(2, 'something') + call append(1, 'two two') + call assert_equal([{'lnum': 2, 'end': 3, 'col': 1, 'added': 0}], s:list) + call listener_flush() + call assert_equal([{'lnum': 2, 'end': 2, 'col': 1, 'added': 1}], s:list) + + " an insert above a previous change causes a flush + call setline(1, ['one one', 'two']) + call listener_flush() + call setline(2, 'something') + call append(0, 'two two') + call assert_equal([{'lnum': 2, 'end': 3, 'col': 1, 'added': 0}], s:list) + call assert_equal('something', s:text) + call listener_flush() + call assert_equal([{'lnum': 1, 'end': 1, 'col': 1, 'added': 1}], s:list) + call assert_equal('two two', s:text) + + " a delete at a previous change that was the last one does not get merged + call setline(1, ['one one', 'two']) + call listener_flush() + let s:list = [] + call setline(2, 'something') + 2del + call assert_equal([{'lnum': 2, 'end': 3, 'col': 1, 'added': 0}], s:list) + call listener_flush() + call assert_equal([{'lnum': 2, 'end': 3, 'col': 1, 'added': -1}], s:list) + + " a delete above a previous change causes a flush + call setline(1, ['one one', 'two']) + call listener_flush() + call setline(2, 'another') + 1del + call assert_equal([{'lnum': 2, 'end': 3, 'col': 1, 'added': 0}], s:list) + call assert_equal(2, s:start) + call assert_equal('another', s:text) + call listener_flush() + call assert_equal([{'lnum': 1, 'end': 2, 'col': 1, 'added': -1}], s:list) + call assert_equal('another', s:text) + + " the "o" command first adds an empty line and then changes it + %del + call setline(1, ['one one', 'two']) + call listener_flush() + let s:list = [] + exe "normal Gofour\<Esc>" + redraw + call assert_equal([{'lnum': 3, 'end': 3, 'col': 1, 'added': 1}, + \ {'lnum': 3, 'end': 4, 'col': 1, 'added': 0}], s:list) + + " Remove last listener + let s:list = [] + call listener_remove(id) + call setline(1, 'asdfasdf') + redraw + call assert_equal([], s:list) + + " Trying to change the list fails + let id = listener_add({b, s, e, a, l -> s:EvilStoreList(l)}) + let s:list3 = [] + call setline(1, 'asdfasdf') + redraw + call assert_equal([{'lnum': 1, 'end': 2, 'col': 1, 'added': 0}], s:list3) + + eval id->listener_remove() + bwipe! +endfunc + +func s:StoreListArgs(buf, start, end, added, list) + let s:buf = a:buf + let s:start = a:start + let s:end = a:end + let s:added = a:added + let s:list = a:list +endfunc + +func Test_listener_args() + new + call setline(1, ['one', 'two']) + let s:list = [] + let id = listener_add('s:StoreListArgs') + + " just one change + call setline(1, 'one one') + call listener_flush() + call assert_equal(bufnr(''), s:buf) + call assert_equal(1, s:start) + call assert_equal(2, s:end) + call assert_equal(0, s:added) + call assert_equal([{'lnum': 1, 'end': 2, 'col': 1, 'added': 0}], s:list) + + " two disconnected changes + call setline(1, ['one', 'two', 'three', 'four']) + call listener_flush() + call setline(1, 'one one') + call setline(3, 'three three') + call listener_flush() + call assert_equal(bufnr(''), s:buf) + call assert_equal(1, s:start) + call assert_equal(4, s:end) + call assert_equal(0, s:added) + call assert_equal([{'lnum': 1, 'end': 2, 'col': 1, 'added': 0}, + \ {'lnum': 3, 'end': 4, 'col': 1, 'added': 0}], s:list) + + " add and remove lines + call setline(1, ['one', 'two', 'three', 'four', 'five', 'six']) + call listener_flush() + call append(2, 'two two') + 4del + call append(5, 'five five') + call listener_flush() + call assert_equal(bufnr(''), s:buf) + call assert_equal(3, s:start) + call assert_equal(6, s:end) + call assert_equal(1, s:added) + call assert_equal([{'lnum': 3, 'end': 3, 'col': 1, 'added': 1}, + \ {'lnum': 4, 'end': 5, 'col': 1, 'added': -1}, + \ {'lnum': 6, 'end': 6, 'col': 1, 'added': 1}], s:list) + + " split a line then insert one, should get two disconnected change lists + call setline(1, 'split here') + call listener_flush() + let s:list = [] + exe "normal 1ggwi\<CR>\<Esc>" + 1 + normal o + call assert_equal([{'lnum': 1, 'end': 2, 'col': 7, 'added': 1}], s:list) + call listener_flush() + call assert_equal([{'lnum': 2, 'end': 2, 'col': 1, 'added': 1}], s:list) + + call listener_remove(id) + bwipe! + + " Invalid arguments + call assert_fails('call listener_add([])', 'E921:') + call assert_fails('call listener_add("s:StoreListArgs", [])', 'E730:') + call assert_fails('call listener_flush([])', 'E730:') +endfunc + +func s:StoreBufList(buf, start, end, added, list) + let s:bufnr = a:buf + let s:list = a:list +endfunc + +func Test_listening_other_buf() + new + call setline(1, ['one', 'two']) + let bufnr = bufnr('') + normal ww + let id = bufnr->listener_add(function('s:StoreBufList')) + let s:list = [] + call setbufline(bufnr, 1, 'hello') + redraw + call assert_equal(bufnr, s:bufnr) + call assert_equal([{'lnum': 1, 'end': 2, 'col': 1, 'added': 0}], s:list) + + call listener_remove(id) + exe "buf " .. bufnr + bwipe! +endfunc + +func Test_listener_garbage_collect() + func MyListener(x, bufnr, start, end, added, changes) + " NOP + endfunc + + new + let id = listener_add(function('MyListener', [{}]), bufnr('')) + call test_garbagecollect_now() + " must not crash caused by invalid memory access + normal ia + call assert_true(v:true) + + call listener_remove(id) + delfunc MyListener + bwipe! +endfunc + +" This verifies the fix for issue #4455 +func Test_listener_caches_buffer_line() + new + inoremap <silent> <CR> <CR><Esc>O + + function EchoChanges(bufnr, start, end, added, changes) + for l:change in a:changes + let text = getbufline(a:bufnr, l:change.lnum, l:change.end-1+l:change.added) + endfor + endfunction + let lid = listener_add("EchoChanges") + set autoindent + set cindent + + call setline(1, ["{", "\tif true {}", "}"]) + exe "normal /{}\nl" + call feedkeys("i\r\e", 'xt') + call assert_equal(["{", "\tif true {", "", "\t}", "}"], getline(1, 5)) + + bwipe! + delfunc EchoChanges + call listener_remove(lid) + iunmap <CR> + set nocindent +endfunc + +" Verify the fix for issue #4908 +func Test_listener_undo_line_number() + function DoIt() + " NOP + endfunction + function EchoChanges(bufnr, start, end, added, changes) + call DoIt() + endfunction + + new + let lid = listener_add("EchoChanges") + call setline(1, ['a', 'b', 'c']) + set undolevels& " start new undo block + call feedkeys("ggcG\<Esc>", 'xt') + undo + + bwipe! + delfunc DoIt + delfunc EchoChanges + call listener_remove(lid) +endfunc + +func Test_listener_undo_delete_all() + new + call setline(1, [1, 2, 3, 4]) + let s:changes = [] + func s:ExtendList(bufnr, start, end, added, changes) + call extend(s:changes, a:changes) + endfunc + let id = listener_add('s:ExtendList') + + set undolevels& " start new undo block + normal! ggdG + undo + call listener_flush() + call assert_equal(2, s:changes->len()) + " delete removes four lines, empty line remains + call assert_equal({'lnum': 1, 'end': 5, 'col': 1, 'added': -4}, s:changes[0]) + " undo replaces empty line and adds 3 lines + call assert_equal({'lnum': 1, 'end': 2, 'col': 1, 'added': 3}, s:changes[1]) + + call listener_remove(id) + delfunc s:ExtendList + unlet s:changes + bwipe! +endfunc + +func Test_listener_cleared_newbuf() + func Listener(bufnr, start, end, added, changes) + let g:gotCalled += 1 + endfunc + new + " check that listening works + let g:gotCalled = 0 + let lid = listener_add("Listener") + call feedkeys("axxx\<Esc>", 'xt') + call listener_flush(bufnr()) + call assert_equal(1, g:gotCalled) + %bwipe! + let bufnr = bufnr() + let b:testing = 123 + let lid = listener_add("Listener") + enew! + " check buffer is reused + call assert_equal(bufnr, bufnr()) + call assert_false(exists('b:testing')) + + " check that listening stops when reusing the buffer + let g:gotCalled = 0 + call feedkeys("axxx\<Esc>", 'xt') + call listener_flush(bufnr()) + call assert_equal(0, g:gotCalled) + unlet g:gotCalled + + bwipe! + delfunc Listener +endfunc + +func Test_col_after_deletion_moved_cur() + func Listener(bufnr, start, end, added, changes) + call assert_equal([#{lnum: 1, end: 2, added: 0, col: 2}], a:changes) + endfunc + new + call setline(1, ['foo']) + let lid = listener_add('Listener') + call feedkeys("lD", 'xt') + call listener_flush() + bwipe! + delfunc Listener +endfunc + +func Test_remove_listener_in_callback() + new + let s:ID = listener_add('Listener') + func Listener(...) + call listener_remove(s:ID) + let g:listener_called = 'yes' + endfunc + call setline(1, ['foo']) + call feedkeys("lD", 'xt') + call listener_flush() + call assert_equal('yes', g:listener_called) + + bwipe! + delfunc Listener + unlet g:listener_called +endfunc + +" When multiple listeners are registered, remove one listener and verify the +" other listener is still called +func Test_remove_one_listener_in_callback() + new + let g:listener1_called = 0 + let g:listener2_called = 0 + let s:ID1 = listener_add('Listener1') + let s:ID2 = listener_add('Listener2') + func Listener1(...) + call listener_remove(s:ID1) + let g:listener1_called += 1 + endfunc + func Listener2(...) + let g:listener2_called += 1 + endfunc + call setline(1, ['foo']) + call feedkeys("~", 'xt') + call listener_flush() + call feedkeys("~", 'xt') + call listener_flush() + call assert_equal(1, g:listener1_called) + call assert_equal(2, g:listener2_called) + + call listener_remove(s:ID2) + bwipe! + delfunc Listener1 + delfunc Listener2 + unlet g:listener1_called + unlet g:listener2_called +endfunc + +func Test_no_change_for_empty_undo() + new + let text = ['some word here', 'second line'] + call setline(1, text) + let g:entries = [] + func Listener(bufnr, start, end, added, changes) + for change in a:changes + call add(g:entries, [change.lnum, change.end, change.added]) + endfor + endfunc + let s:ID = listener_add('Listener') + let @a = "one line\ntwo line\nthree line" + set undolevels& " start new undo block + call feedkeys('fwviw"ap', 'xt') + call listener_flush(bufnr()) + " first change deletes "word", second change inserts the register + call assert_equal([[1, 2, 0], [1, 2, 2]], g:entries) + let g:entries = [] + + set undolevels& " start new undo block + undo + call listener_flush(bufnr()) + call assert_equal([[1, 4, -2]], g:entries) + call assert_equal(text, getline(1, 2)) + + call listener_remove(s:ID) + bwipe! + unlet g:entries + delfunc Listener +endfunc + + +" vim: shiftwidth=2 sts=2 expandtab |