diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/functional/autocmd/termclose_spec.lua | 28 | ||||
-rw-r--r-- | test/functional/dict_notifications_spec.lua | 257 | ||||
-rw-r--r-- | test/functional/ex_cmds/oldfiles_spec.lua | 97 | ||||
-rw-r--r-- | test/functional/ex_cmds/profile_spec.lua | 51 | ||||
-rw-r--r-- | test/functional/ex_cmds/wundo_spec.lua | 17 | ||||
-rw-r--r-- | test/functional/provider/python3_spec.lua | 1 | ||||
-rw-r--r-- | test/functional/provider/python_spec.lua | 1 | ||||
-rw-r--r-- | test/functional/shada/marks_spec.lua | 2 | ||||
-rw-r--r-- | test/functional/terminal/ex_terminal_spec.lua | 6 | ||||
-rw-r--r-- | test/functional/terminal/scrollback_spec.lua | 2 | ||||
-rw-r--r-- | test/functional/terminal/tui_spec.lua | 28 | ||||
-rw-r--r-- | test/unit/buffer_spec.lua | 97 |
12 files changed, 578 insertions, 9 deletions
diff --git a/test/functional/autocmd/termclose_spec.lua b/test/functional/autocmd/termclose_spec.lua new file mode 100644 index 0000000000..265d857a42 --- /dev/null +++ b/test/functional/autocmd/termclose_spec.lua @@ -0,0 +1,28 @@ +local helpers = require('test.functional.helpers') +local Screen = require('test.functional.ui.screen') + +local clear, eval, execute, feed, nvim, nvim_dir = helpers.clear, helpers.eval, +helpers.execute, helpers.feed, helpers.nvim, helpers.nvim_dir +local wait = helpers.wait + +describe('TermClose event', function() + before_each(function() + clear() + nvim('set_option', 'shell', nvim_dir .. '/shell-test') + nvim('set_option', 'shellcmdflag', 'EXE') + screen = Screen.new(20, 4) + screen:attach(false) + end) + + it('works as expected', function() + execute('autocmd TermClose * echomsg "TermClose works!"') + execute('terminal') + feed('<c-\\><c-n>') + screen:expect([[ + ready $ | + [Process exited 0] | + ^ | + TermClose works! | + ]]) + end) +end) diff --git a/test/functional/dict_notifications_spec.lua b/test/functional/dict_notifications_spec.lua new file mode 100644 index 0000000000..2540929126 --- /dev/null +++ b/test/functional/dict_notifications_spec.lua @@ -0,0 +1,257 @@ +local helpers = require('test.functional.helpers') +local clear, nvim, source = helpers.clear, helpers.nvim, helpers.source +local eq, next_msg = helpers.eq, helpers.next_message +local exc_exec = helpers.exc_exec + + +describe('dictionary change notifications', function() + local channel + + setup(function() + clear() + channel = nvim('get_api_info')[1] + nvim('set_var', 'channel', channel) + end) + + -- the same set of tests are applied to top-level dictionaries(g:, b:, w: and + -- t:) and a dictionary variable, so we generate them in the following + -- function. + local function gentests(dict_expr, dict_expr_suffix, dict_init) + if not dict_expr_suffix then + dict_expr_suffix = '' + end + + local function update(opval, key) + if not key then + key = 'watched' + end + if opval == '' then + nvim('command', "unlet "..dict_expr..dict_expr_suffix..key) + else + nvim('command', "let "..dict_expr..dict_expr_suffix..key.." "..opval) + end + end + + local function verify_echo() + -- helper to verify that no notifications are sent after certain change + -- to a dict + nvim('command', "call rpcnotify(g:channel, 'echo')") + eq({'notification', 'echo', {}}, next_msg()) + end + + local function verify_value(vals, key) + if not key then + key = 'watched' + end + eq({'notification', 'values', {key, vals}}, next_msg()) + end + + describe('watcher', function() + if dict_init then + setup(function() + source(dict_init) + end) + end + + before_each(function() + source([[ + function! g:Changed(dict, key, value) + if a:dict != ]]..dict_expr..[[ | + throw 'invalid dict' + endif + call rpcnotify(g:channel, 'values', a:key, a:value) + endfunction + call dictwatcheradd(]]..dict_expr..[[, "watched", "g:Changed") + call dictwatcheradd(]]..dict_expr..[[, "watched2", "g:Changed") + ]]) + end) + + after_each(function() + source([[ + call dictwatcherdel(]]..dict_expr..[[, "watched", "g:Changed") + call dictwatcherdel(]]..dict_expr..[[, "watched2", "g:Changed") + ]]) + update('= "test"') + update('= "test2"', 'watched2') + update('', 'watched2') + update('') + verify_echo() + end) + + it('is not triggered when unwatched keys are updated', function() + update('= "noop"', 'unwatched') + update('.= "noop2"', 'unwatched') + update('', 'unwatched') + verify_echo() + end) + + it('is triggered by remove()', function() + update('= "test"') + verify_value({new = 'test'}) + nvim('command', 'call remove('..dict_expr..', "watched")') + verify_value({old = 'test'}) + end) + + it('is triggered by extend()', function() + update('= "xtend"') + verify_value({new = 'xtend'}) + nvim('command', [[ + call extend(]]..dict_expr..[[, {'watched': 'xtend2', 'watched2': 5, 'watched3': 'a'}) + ]]) + verify_value({old = 'xtend', new = 'xtend2'}) + verify_value({new = 5}, 'watched2') + update('') + verify_value({old = 'xtend2'}) + update('', 'watched2') + verify_value({old = 5}, 'watched2') + update('', 'watched3') + verify_echo() + end) + + it('is triggered with key patterns', function() + source([[ + call dictwatcheradd(]]..dict_expr..[[, "wat*", "g:Changed") + ]]) + update('= 1') + verify_value({new = 1}) + verify_value({new = 1}) + update('= 3', 'watched2') + verify_value({new = 3}, 'watched2') + verify_value({new = 3}, 'watched2') + verify_echo() + source([[ + call dictwatcherdel(]]..dict_expr..[[, "wat*", "g:Changed") + ]]) + -- watch every key pattern + source([[ + call dictwatcheradd(]]..dict_expr..[[, "*", "g:Changed") + ]]) + update('= 3', 'another_key') + update('= 4', 'another_key') + update('', 'another_key') + update('= 2') + verify_value({new = 3}, 'another_key') + verify_value({old = 3, new = 4}, 'another_key') + verify_value({old = 4}, 'another_key') + verify_value({old = 1, new = 2}) + verify_value({old = 1, new = 2}) + verify_echo() + source([[ + call dictwatcherdel(]]..dict_expr..[[, "*", "g:Changed") + ]]) + end) + + -- test a sequence of updates of different types to ensure proper memory + -- management(with ASAN) + local function test_updates(tests) + it('test change sequence', function() + local input, output + for i = 1, #tests do + input, output = unpack(tests[i]) + update(input) + verify_value(output) + end + end) + end + + test_updates({ + {'= 3', {new = 3}}, + {'= 6', {old = 3, new = 6}}, + {'+= 3', {old = 6, new = 9}}, + {'', {old = 9}} + }) + + test_updates({ + {'= "str"', {new = 'str'}}, + {'= "str2"', {old = 'str', new = 'str2'}}, + {'.= "2str"', {old = 'str2', new = 'str22str'}}, + {'', {old = 'str22str'}} + }) + + test_updates({ + {'= [1, 2]', {new = {1, 2}}}, + {'= [1, 2, 3]', {old = {1, 2}, new = {1, 2, 3}}}, + -- the += will update the list in place, so old and new are the same + {'+= [4, 5]', {old = {1, 2, 3, 4, 5}, new = {1, 2, 3, 4, 5}}}, + {'', {old = {1, 2, 3, 4 ,5}}} + }) + + test_updates({ + {'= {"k": "v"}', {new = {k = 'v'}}}, + {'= {"k1": 2}', {old = {k = 'v'}, new = {k1 = 2}}}, + {'', {old = {k1 = 2}}}, + }) + end) + end + + gentests('g:') + gentests('b:') + gentests('w:') + gentests('t:') + gentests('g:dict_var', '.', 'let g:dict_var = {}') + + describe('multiple watchers on the same dict/key', function() + setup(function() + source([[ + function! g:Watcher1(dict, key, value) + call rpcnotify(g:channel, '1', a:key, a:value) + endfunction + function! g:Watcher2(dict, key, value) + call rpcnotify(g:channel, '2', a:key, a:value) + endfunction + call dictwatcheradd(g:, "key", "g:Watcher1") + call dictwatcheradd(g:, "key", "g:Watcher2") + ]]) + end) + + it('invokes all callbacks when the key is changed', function() + nvim('command', 'let g:key = "value"') + eq({'notification', '1', {'key', {new = 'value'}}}, next_msg()) + eq({'notification', '2', {'key', {new = 'value'}}}, next_msg()) + end) + + it('only removes watchers that fully match dict, key and callback', function() + nvim('command', 'call dictwatcherdel(g:, "key", "g:Watcher1")') + nvim('command', 'let g:key = "v2"') + eq({'notification', '2', {'key', {old = 'value', new = 'v2'}}}, next_msg()) + end) + end) + + describe('errors', function() + -- WARNING: This suite depends on the above tests + it('fails to remove if no watcher with matching callback is found', function() + eq("Vim(call):Couldn't find a watcher matching key and callback", + exc_exec('call dictwatcherdel(g:, "key", "g:Watcher1")')) + end) + + it('fails to remove if no watcher with matching key is found', function() + eq("Vim(call):Couldn't find a watcher matching key and callback", + exc_exec('call dictwatcherdel(g:, "invalid_key", "g:Watcher2")')) + end) + + it("fails to add/remove if the callback doesn't exist", function() + eq("Vim(call):Function g:InvalidCb doesn't exist", + exc_exec('call dictwatcheradd(g:, "key", "g:InvalidCb")')) + eq("Vim(call):Function g:InvalidCb doesn't exist", + exc_exec('call dictwatcherdel(g:, "key", "g:InvalidCb")')) + end) + + it('fails with empty keys', function() + eq("Vim(call):E713: Cannot use empty key for Dictionary", + exc_exec('call dictwatcheradd(g:, "", "g:Watcher1")')) + eq("Vim(call):E713: Cannot use empty key for Dictionary", + exc_exec('call dictwatcherdel(g:, "", "g:Watcher1")')) + end) + + it('fails to replace a watcher function', function() + source([[ + function! g:ReplaceWatcher2() + function! g:Watcher2() + endfunction + endfunction + ]]) + eq("Vim(function):E127: Cannot redefine function Watcher2: It is in use", + exc_exec('call g:ReplaceWatcher2()')) + end) + end) +end) diff --git a/test/functional/ex_cmds/oldfiles_spec.lua b/test/functional/ex_cmds/oldfiles_spec.lua new file mode 100644 index 0000000000..e02f42cd12 --- /dev/null +++ b/test/functional/ex_cmds/oldfiles_spec.lua @@ -0,0 +1,97 @@ +local Screen = require('test.functional.ui.screen') +local helpers = require('test.functional.helpers') + +local buf, eq, execute = helpers.curbufmeths, helpers.eq, helpers.execute +local feed, nvim, nvim_prog = helpers.feed, helpers.nvim, helpers.nvim_prog +local ok, set_session, spawn = helpers.ok, helpers.set_session, helpers.spawn + +local shada_file = 'test.shada' + +-- +-- helpers.clear() uses "-i NONE", which is not useful for this test. +-- +local function _clear() + if session then + session:exit(0) + end + set_session(spawn({nvim_prog, + '-u', 'NONE', + '--cmd', 'set noswapfile undodir=. directory=. viewdir=. backupdir=.', + '--embed'})) +end + +describe(':oldfiles', function() + before_each(_clear) + + after_each(function() + os.remove(shada_file) + end) + + local function add_padding(s) + return s .. string.rep(' ', 96 - string.len(s)) + end + + it('shows most recently used files', function() + screen = Screen.new(100, 5) + screen:attach() + execute('edit testfile1') + execute('edit testfile2') + execute('wshada ' .. shada_file) + execute('rshada! ' .. shada_file) + local oldfiles = helpers.meths.get_vvar('oldfiles') + execute('oldfiles') + screen:expect([[ + testfile2 | + 1: ]].. add_padding(oldfiles[1]) ..[[ | + 2: ]].. add_padding(oldfiles[2]) ..[[ | + | + Press ENTER or type command to continue^ | + ]]) + end) +end) + +describe(':oldfiles!', function() + local filename + local filename2 + local oldfiles + + before_each(function() + _clear() + execute('edit testfile1') + filename = buf.get_name() + execute('edit testfile2') + filename2 = buf.get_name() + execute('wshada ' .. shada_file) + _clear() + execute('rshada! ' .. shada_file) + + -- Ensure v:oldfiles isn't busted. Since things happen so fast, + -- the ordering of v:oldfiles is unstable (it uses qsort() under-the-hood). + -- Let's verify the contents and the length of v:oldfiles before moving on. + oldfiles = helpers.meths.get_vvar('oldfiles') + eq(2, #oldfiles) + ok(filename == oldfiles[1] or filename == oldfiles[2]) + ok(filename2 == oldfiles[1] or filename2 == oldfiles[2]) + + execute('oldfiles!') + end) + + after_each(function() + os.remove(shada_file) + end) + + it('provides a prompt and edits the chosen file', function() + feed('2<cr>') + eq(oldfiles[2], buf.get_name()) + end) + + it('provides a prompt and does nothing on <cr>', function() + feed('<cr>') + eq('', buf.get_name()) + end) + + it('provides a prompt and does nothing if choice is out-of-bounds', function() + feed('3<cr>') + eq('', buf.get_name()) + end) +end) diff --git a/test/functional/ex_cmds/profile_spec.lua b/test/functional/ex_cmds/profile_spec.lua new file mode 100644 index 0000000000..721669e73b --- /dev/null +++ b/test/functional/ex_cmds/profile_spec.lua @@ -0,0 +1,51 @@ +require('os') +require('lfs') + +local helpers = require('test.functional.helpers') +local eval = helpers.eval +local command = helpers.command +local eq, neq = helpers.eq, helpers.neq +local tempfile = os.tmpname() + +-- os.tmpname() also creates the file on POSIX systems. Remove it again. +-- We just need the name, ignoring any race conditions. +if lfs.attributes(tempfile, 'uid') then + os.remove(tempfile) +end + +local function assert_file_exists(filepath) + -- Use 2-argument lfs.attributes() so no extra table gets created. + -- We don't really care for the uid. + neq(nil, lfs.attributes(filepath, 'uid')) +end + +local function assert_file_exists_not(filepath) + eq(nil, lfs.attributes(filepath, 'uid')) +end + +describe(':profile', function() + before_each(helpers.clear) + + after_each(function() + if lfs.attributes(tempfile, 'uid') ~= nil then + os.remove(tempfile) + end + end) + + it('dump', function() + eq(0, eval('v:profiling')) + command('profile start ' .. tempfile) + eq(1, eval('v:profiling')) + assert_file_exists_not(tempfile) + command('profile dump') + assert_file_exists(tempfile) + end) + + it('stop', function() + command('profile start ' .. tempfile) + assert_file_exists_not(tempfile) + command('profile stop') + assert_file_exists(tempfile) + eq(0, eval('v:profiling')) + end) +end) diff --git a/test/functional/ex_cmds/wundo_spec.lua b/test/functional/ex_cmds/wundo_spec.lua index d8bd8a7031..c3147e1c0f 100644 --- a/test/functional/ex_cmds/wundo_spec.lua +++ b/test/functional/ex_cmds/wundo_spec.lua @@ -1,9 +1,9 @@ --- Specs for --- :wundo +-- Specs for :wundo and underlying functions local helpers = require('test.functional.helpers') -local execute, eq, clear, eval, feed = - helpers.execute, helpers.eq, helpers.clear, helpers.eval, helpers.feed +local execute, clear, eval, feed, spawn, nvim_prog, set_session = + helpers.execute, helpers.clear, helpers.eval, helpers.feed, helpers.spawn, + helpers.nvim_prog, helpers.set_session describe(':wundo', function() @@ -16,5 +16,14 @@ describe(':wundo', function() os.remove(eval('getcwd()') .. '/foo') --cleanup end) +end) +describe('u_* functions', function() + it('safely fail on new, non-empty buffer', function() + local session = spawn({nvim_prog, '-u', 'NONE', '-i', 'NONE', '--embed', + '-c', 'set undodir=. undofile'}) + set_session(session) + execute('echo "True"') -- Should not error out due to crashed Neovim + session:exit(0) + end) end) diff --git a/test/functional/provider/python3_spec.lua b/test/functional/provider/python3_spec.lua index 5ecc1a0a91..a94880d4a2 100644 --- a/test/functional/provider/python3_spec.lua +++ b/test/functional/provider/python3_spec.lua @@ -4,6 +4,7 @@ local eq, clear, insert = helpers.eq, helpers.clear, helpers.insert local expect, write_file = helpers.expect, helpers.write_file do + clear() command('let [g:interp, g:errors] = provider#pythonx#Detect(3)') local errors = eval('g:errors') if errors ~= '' then diff --git a/test/functional/provider/python_spec.lua b/test/functional/provider/python_spec.lua index f37c16a26a..da45d6aa00 100644 --- a/test/functional/provider/python_spec.lua +++ b/test/functional/provider/python_spec.lua @@ -4,6 +4,7 @@ local eq, clear, insert = helpers.eq, helpers.clear, helpers.insert local expect, write_file = helpers.expect, helpers.write_file do + clear() command('let [g:interp, g:errors] = provider#pythonx#Detect(2)') local errors = eval('g:errors') if errors ~= '' then diff --git a/test/functional/shada/marks_spec.lua b/test/functional/shada/marks_spec.lua index f03b330fb5..6818844ebd 100644 --- a/test/functional/shada/marks_spec.lua +++ b/test/functional/shada/marks_spec.lua @@ -108,6 +108,7 @@ describe('ShaDa support code', function() nvim_command('qall') reset() local oldfiles = meths.get_vvar('oldfiles') + table.sort(oldfiles) eq(2, #oldfiles) eq(testfilename, oldfiles[1]:sub(-#testfilename)) eq(testfilename_2, oldfiles[2]:sub(-#testfilename_2)) @@ -115,6 +116,7 @@ describe('ShaDa support code', function() eq(tf_full_2, oldfiles[2]) nvim_command('rshada!') local oldfiles = meths.get_vvar('oldfiles') + table.sort(oldfiles) eq(2, #oldfiles) eq(testfilename, oldfiles[1]:sub(-#testfilename)) eq(testfilename_2, oldfiles[2]:sub(-#testfilename_2)) diff --git a/test/functional/terminal/ex_terminal_spec.lua b/test/functional/terminal/ex_terminal_spec.lua index 3855cf4b65..611ba55793 100644 --- a/test/functional/terminal/ex_terminal_spec.lua +++ b/test/functional/terminal/ex_terminal_spec.lua @@ -22,7 +22,7 @@ describe(':terminal', function() wait() screen:expect([[ ready $ | - [Program exited, press any key to close] | + [Process exited 0] | | | | @@ -37,7 +37,7 @@ describe(':terminal', function() screen:expect([[ ready $ echo hi | | - [Program exited, press any key to close] | + [Process exited 0] | | | | @@ -51,7 +51,7 @@ describe(':terminal', function() screen:expect([[ ready $ echo 'hello' \ "world" | | - [Program exited, press any key to close] | + [Process exited 0] | | | | diff --git a/test/functional/terminal/scrollback_spec.lua b/test/functional/terminal/scrollback_spec.lua index 87cc9a8266..4b56698520 100644 --- a/test/functional/terminal/scrollback_spec.lua +++ b/test/functional/terminal/scrollback_spec.lua @@ -340,7 +340,7 @@ describe('terminal prints more lines than the screen height and exits', function line8 | line9 | | - [Program exited, press any key to close] | + [Process exited 0] | -- TERMINAL -- | ]]) feed('<cr>') diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index 0c4b80fdd2..9a1fdfca55 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -148,6 +148,32 @@ describe('tui', function() -- TERMINAL -- | ]]) end) + + it('can handle focus events', function() + execute('autocmd FocusGained * echo "gained"') + execute('autocmd FocusLost * echo "lost"') + feed('\x1b[I') + screen:expect([[ + {1: } | + ~ | + ~ | + ~ | + [No Name] | + gained | + -- TERMINAL -- | + ]]) + + feed('\x1b[O') + screen:expect([[ + {1: } | + ~ | + ~ | + ~ | + [No Name] | + lost | + -- TERMINAL -- | + ]]) + end) end) describe('tui with non-tty file descriptors', function() @@ -167,7 +193,7 @@ describe('tui with non-tty file descriptors', function() :q | abc | | - [Program exited, press any key to close] | + [Process exited 0] | | -- TERMINAL -- | ]]) diff --git a/test/unit/buffer_spec.lua b/test/unit/buffer_spec.lua index e0e2b827e9..05fba684d6 100644 --- a/test/unit/buffer_spec.lua +++ b/test/unit/buffer_spec.lua @@ -1,9 +1,15 @@ + +local assert = require("luassert") local helpers = require("test.unit.helpers") local to_cstr = helpers.to_cstr local eq = helpers.eq +local neq = helpers.neq +local globals = helpers.cimport("./src/nvim/globals.h") local buffer = helpers.cimport("./src/nvim/buffer.h") +local fileio = helpers.cimport("./src/nvim/fileio.h") +local ex_docmd = helpers.cimport("./src/nvim/ex_docmd.h") local window = helpers.cimport("./src/nvim/window.h") local option = helpers.cimport("./src/nvim/option.h") @@ -206,4 +212,95 @@ describe('buffer functions', function() close_buffer(NULL, buf2, buffer.DOBUF_WIPE, 0) end) end) + + describe('build_stl_str_hl', function() + + output_buffer = to_cstr(string.rep(" ", 100)) + + local build_stl_str_hl = function(pat) + return buffer.build_stl_str_hl(globals.curwin, + output_buffer, + 100, + to_cstr(pat), + false, + 32, + 80, + NULL, + NULL) + end + + it('should copy plain text', function() + local width = build_stl_str_hl("this is a test") + + eq(14, width) + eq("this is a test", helpers.ffi.string(output_buffer, width)) + + end) + + it('should print no file name', function() + local width = build_stl_str_hl("%f") + + eq(9, width) + eq("[No Name]", helpers.ffi.string(output_buffer, width)) + + end) + + it('should print the relative file name', function() + buffer.setfname(globals.curbuf, to_cstr("Makefile"), NULL, 1) + local width = build_stl_str_hl("%f") + + eq(8, width) + eq("Makefile", helpers.ffi.string(output_buffer, width)) + + end) + + it('should print the full file name', function() + buffer.setfname(globals.curbuf, to_cstr("Makefile"), NULL, 1) + + local width = build_stl_str_hl("%F") + + assert.is_true(8 < width) + neq(NULL, string.find(helpers.ffi.string(output_buffer, width), "Makefile")) + + end) + + it('should print the tail file name', function() + buffer.setfname(globals.curbuf, to_cstr("src/nvim/buffer.c"), NULL, 1) + + local width = build_stl_str_hl("%t") + + eq(8, width) + eq("buffer.c", helpers.ffi.string(output_buffer, width)) + + end) + + it('should print the buffer number', function() + buffer.setfname(globals.curbuf, to_cstr("src/nvim/buffer.c"), NULL, 1) + + local width = build_stl_str_hl("%n") + + eq(1, width) + eq("1", helpers.ffi.string(output_buffer, width)) + end) + + it('should print the current line number in the buffer', function() + buffer.setfname(globals.curbuf, to_cstr("test/unit/buffer_spec.lua"), NULL, 1) + + local width = build_stl_str_hl("%l") + + eq(1, width) + eq("0", helpers.ffi.string(output_buffer, width)) + + end) + + it('should print the number of lines in the buffer', function() + buffer.setfname(globals.curbuf, to_cstr("test/unit/buffer_spec.lua"), NULL, 1) + + local width = build_stl_str_hl("%L") + + eq(1, width) + eq("1", helpers.ffi.string(output_buffer, width)) + + end) + end) end) |