diff options
Diffstat (limited to 'test/functional/ex_cmds')
20 files changed, 806 insertions, 78 deletions
diff --git a/test/functional/ex_cmds/append_spec.lua b/test/functional/ex_cmds/append_spec.lua index 2d5ab8e8c8..0a4d701794 100644 --- a/test/functional/ex_cmds/append_spec.lua +++ b/test/functional/ex_cmds/append_spec.lua @@ -1,4 +1,4 @@ -local helpers = require('test.functional.helpers') +local helpers = require('test.functional.helpers')(after_each) local eq = helpers.eq local feed = helpers.feed @@ -22,8 +22,8 @@ local cmdtest = function(cmd, prep, ret1) command(cmd .. '\nabc\ndef\n') eq(ret1, buffer_contents()) end) - -- Used to crash because this invokes history processing which uses - -- hist_char2type which after fdb68e35e4c729c7ed097d8ade1da29e5b3f4b31 + -- Used to crash because this invokes history processing which uses + -- hist_char2type which after fdb68e35e4c729c7ed097d8ade1da29e5b3f4b31 -- crashed. it(cmd .. 's' .. prep .. ' the current line by default when feeding', function() diff --git a/test/functional/ex_cmds/arg_spec.lua b/test/functional/ex_cmds/arg_spec.lua new file mode 100644 index 0000000000..e11b90532f --- /dev/null +++ b/test/functional/ex_cmds/arg_spec.lua @@ -0,0 +1,30 @@ +local helpers = require("test.functional.helpers")(after_each) +local eq, execute, funcs = helpers.eq, helpers.execute, helpers.funcs +local ok = helpers.ok +local clear = helpers.clear + +describe(":argument", function() + before_each(function() + clear() + end) + + it("does not restart :terminal buffer", function() + execute("terminal") + helpers.feed([[<C-\><C-N>]]) + execute("argadd") + helpers.feed([[<C-\><C-N>]]) + local bufname_before = funcs.bufname("%") + local bufnr_before = funcs.bufnr("%") + helpers.ok(nil ~= string.find(bufname_before, "^term://")) -- sanity + + execute("argument 1") + helpers.feed([[<C-\><C-N>]]) + + local bufname_after = funcs.bufname("%") + local bufnr_after = funcs.bufnr("%") + eq("\n["..bufname_before.."] ", helpers.eval('execute("args")')) + ok(funcs.line('$') > 1) + eq(bufname_before, bufname_after) + eq(bufnr_before, bufnr_after) + end) +end) diff --git a/test/functional/ex_cmds/bang_filter_spec.lua b/test/functional/ex_cmds/bang_filter_spec.lua new file mode 100644 index 0000000000..a320e6d018 --- /dev/null +++ b/test/functional/ex_cmds/bang_filter_spec.lua @@ -0,0 +1,51 @@ +-- Specs for bang/filter commands + +local helpers = require('test.functional.helpers')(after_each) +local feed, execute, clear = helpers.feed, helpers.execute, helpers.clear +local mkdir, write_file, rmdir = helpers.mkdir, helpers.write_file, helpers.rmdir + +if helpers.pending_win32(pending) then return end + +local Screen = require('test.functional.ui.screen') + + +describe('issues', function() + local screen + + before_each(function() + clear() + rmdir('bang_filter_spec') + mkdir('bang_filter_spec') + write_file('bang_filter_spec/f1', 'f1') + write_file('bang_filter_spec/f2', 'f2') + write_file('bang_filter_spec/f3', 'f3') + screen = Screen.new() + screen:attach() + end) + + after_each(function() + rmdir('bang_filter_spec') + end) + + it('#3269 Last line of shell output is not truncated', function() + execute([[nnoremap <silent>\l :!ls bang_filter_spec<cr>]]) + feed([[\l]]) + screen:expect([[ + ~ | + ~ | + ~ | + ~ | + ~ | + ~ | + ~ | + ~ | + :!ls bang_filter_spec | + | + f1 | + f2 | + f3 | + Press ENTER or type command to continue^ | + ]]) + end) + +end) diff --git a/test/functional/ex_cmds/cd_spec.lua b/test/functional/ex_cmds/cd_spec.lua index 69467632a4..5bf4d22d0f 100644 --- a/test/functional/ex_cmds/cd_spec.lua +++ b/test/functional/ex_cmds/cd_spec.lua @@ -1,49 +1,168 @@ -- Specs for :cd, :tcd, :lcd and getcwd() -local helpers = require('test.functional.helpers') -local execute, eq, clear, eval, exc_exec = - helpers.execute, helpers.eq, helpers.clear, helpers.eval, helpers.exc_exec local lfs = require('lfs') +local helpers = require('test.functional.helpers')(after_each) + +local eq = helpers.eq +local call = helpers.call +local clear = helpers.clear +local execute = helpers.execute +local exc_exec = helpers.exc_exec + +if helpers.pending_win32(pending) then return end -- These directories will be created for testing local directories = { - 'Xtest-functional-ex_cmds-cd_spec.1', -- Tab - 'Xtest-functional-ex_cmds-cd_spec.2', -- Window - 'Xtest-functional-ex_cmds-cd_spec.3', -- New global + tab = 'Xtest-functional-ex_cmds-cd_spec.tab', -- Tab + window = 'Xtest-functional-ex_cmds-cd_spec.window', -- Window + global = 'Xtest-functional-ex_cmds-cd_spec.global', -- New global } -- Shorthand writing to get the current working directory -local cwd = function() return eval('getcwd( )') end -- effective working dir -local wcwd = function() return eval('getcwd( 0 )') end -- window dir -local tcwd = function() return eval('getcwd(-1, 0)') end -- tab dir ---local gcwd = function() return eval('getcwd(-1, -1)') end -- global dir +local cwd = function(...) return call('getcwd', ...) end -- effective working dir +local wcwd = function() return cwd(0) end -- window dir +local tcwd = function() return cwd(-1, 0) end -- tab dir -- Same, except these tell us if there is a working directory at all ---local lwd = function() return eval('haslocaldir( )') end -- effective working dir -local wlwd = function() return eval('haslocaldir( 0 )') end -- window dir -local tlwd = function() return eval('haslocaldir(-1, 0)') end -- tab dir +local lwd = function(...) return call('haslocaldir', ...) end -- effective working dir +local wlwd = function() return lwd(0) end -- window dir +local tlwd = function() return lwd(-1, 0) end -- tab dir --local glwd = function() return eval('haslocaldir(-1, -1)') end -- global dir -- Test both the `cd` and `chdir` variants for _, cmd in ipairs {'cd', 'chdir'} do - describe(':*' .. cmd, function() + describe(':' .. cmd, function() before_each(function() clear() - for _, d in ipairs(directories) do + for _, d in pairs(directories) do lfs.mkdir(d) end + directories.start = cwd() end) after_each(function() - for _, d in ipairs(directories) do + for _, d in pairs(directories) do lfs.rmdir(d) end end) - it('works', function() - -- Store the initial working directory - local globalDir = cwd() + describe('using explicit scope', function() + it('for window', function() + local globalDir = directories.start + local globalwin = call('winnr') + local tabnr = call('tabpagenr') + + -- Everything matches globalDir to start + eq(globalDir, cwd(globalwin)) + eq(globalDir, cwd(globalwin, tabnr)) + eq(0, lwd(globalwin)) + eq(0, lwd(globalwin, tabnr)) + + execute('bot split') + local localwin = call('winnr') + -- Initial window is still using globalDir + eq(globalDir, cwd(localwin)) + eq(globalDir, cwd(localwin, tabnr)) + eq(0, lwd(globalwin)) + eq(0, lwd(globalwin, tabnr)) + + execute('silent l' .. cmd .. ' ' .. directories.window) + -- From window with local dir, the original window + -- is still reporting the global dir + eq(globalDir, cwd(globalwin)) + eq(globalDir, cwd(globalwin, tabnr)) + eq(0, lwd(globalwin)) + eq(0, lwd(globalwin, tabnr)) + + -- Window with local dir reports as such + eq(globalDir .. '/' .. directories.window, cwd(localwin)) + eq(globalDir .. '/' .. directories.window, cwd(localwin, tabnr)) + eq(1, lwd(localwin)) + eq(1, lwd(localwin, tabnr)) + + execute('tabnew') + -- From new tab page, original window reports global dir + eq(globalDir, cwd(globalwin, tabnr)) + eq(0, lwd(globalwin, tabnr)) + + -- From new tab page, local window reports as such + eq(globalDir .. '/' .. directories.window, cwd(localwin, tabnr)) + eq(1, lwd(localwin, tabnr)) + end) + + it('for tab page', function() + local globalDir = directories.start + local globaltab = call('tabpagenr') + + -- Everything matches globalDir to start + eq(globalDir, cwd(-1, 0)) + eq(globalDir, cwd(-1, globaltab)) + eq(0, lwd(-1, 0)) + eq(0, lwd(-1, globaltab)) + + execute('tabnew') + execute('silent t' .. cmd .. ' ' .. directories.tab) + local localtab = call('tabpagenr') + + -- From local tab page, original tab reports globalDir + eq(globalDir, cwd(-1, globaltab)) + eq(0, lwd(-1, globaltab)) + + -- new tab reports local + eq(globalDir .. '/' .. directories.tab, cwd(-1, 0)) + eq(globalDir .. '/' .. directories.tab, cwd(-1, localtab)) + eq(1, lwd(-1, 0)) + eq(1, lwd(-1, localtab)) + + execute('tabnext') + -- From original tab page, local reports as such + eq(globalDir .. '/' .. directories.tab, cwd(-1, localtab)) + eq(1, lwd(-1, localtab)) + end) + end) + + describe('getcwd(-1, -1)', function() + it('works', function() + eq(directories.start, cwd(-1, -1)) + eq(0, lwd(-1, -1)) + end) + it('works with tab-local pwd', function() + execute('silent t' .. cmd .. ' ' .. directories.tab) + eq(directories.start, cwd(-1, -1)) + eq(0, lwd(-1, -1)) + end) + + it('works with window-local pwd', function() + execute('silent l' .. cmd .. ' ' .. directories.window) + eq(directories.start, cwd(-1, -1)) + eq(0, lwd(-1, -1)) + end) + end) + + describe('Local directory gets inherited', function() + it('by tabs', function() + local globalDir = directories.start + + -- Create a new tab and change directory + execute('tabnew') + execute('silent t' .. cmd .. ' ' .. directories.tab) + eq(globalDir .. '/' .. directories.tab, tcwd()) + + -- Create a new tab and verify it has inherited the directory + execute('tabnew') + eq(globalDir .. '/' .. directories.tab, tcwd()) + + -- Change tab and change back, verify that directories are correct + execute('tabnext') + eq(globalDir, tcwd()) + execute('tabprevious') + eq(globalDir .. '/' .. directories.tab, tcwd()) + end) + end) + + it('works', function() + local globalDir = directories.start -- Create a new tab first and verify that is has the same working dir execute('tabnew') eq(globalDir, cwd()) @@ -53,8 +172,8 @@ for _, cmd in ipairs {'cd', 'chdir'} do eq(0, wlwd()) -- Change tab-local working directory and verify it is different - execute('silent t' .. cmd .. ' ' .. directories[1]) - eq(globalDir .. '/' .. directories[1], cwd()) + execute('silent t' .. cmd .. ' ' .. directories.tab) + eq(globalDir .. '/' .. directories.tab, cwd()) eq(cwd(), tcwd()) -- working directory maches tab directory eq(1, tlwd()) eq(cwd(), wcwd()) -- still no window-directory @@ -64,16 +183,16 @@ for _, cmd in ipairs {'cd', 'chdir'} do execute('new') eq(1, tlwd()) -- Still tab-local working directory eq(0, wlwd()) -- Still no window-local working directory - eq(globalDir .. '/' .. directories[1], cwd()) - execute('silent l' .. cmd .. ' ../' .. directories[2]) - eq(globalDir .. '/' .. directories[2], cwd()) - eq(globalDir .. '/' .. directories[1], tcwd()) + eq(globalDir .. '/' .. directories.tab, cwd()) + execute('silent l' .. cmd .. ' ../' .. directories.window) + eq(globalDir .. '/' .. directories.window, cwd()) + eq(globalDir .. '/' .. directories.tab, tcwd()) eq(1, wlwd()) -- Verify the first window still has the tab local directory execute('wincmd w') - eq(globalDir .. '/' .. directories[1], cwd()) - eq(globalDir .. '/' .. directories[1], tcwd()) + eq(globalDir .. '/' .. directories.tab, cwd()) + eq(globalDir .. '/' .. directories.tab, tcwd()) eq(0, wlwd()) -- No window-local directory -- Change back to initial tab and verify working directory has stayed @@ -83,11 +202,11 @@ for _, cmd in ipairs {'cd', 'chdir'} do eq(0, wlwd()) -- Verify global changes don't affect local ones - execute('silent ' .. cmd .. ' ' .. directories[3]) - eq(globalDir .. '/' .. directories[3], cwd()) + execute('silent ' .. cmd .. ' ' .. directories.global) + eq(globalDir .. '/' .. directories.global, cwd()) execute('tabnext') - eq(globalDir .. '/' .. directories[1], cwd()) - eq(globalDir .. '/' .. directories[1], tcwd()) + eq(globalDir .. '/' .. directories.tab, cwd()) + eq(globalDir .. '/' .. directories.tab, tcwd()) eq(0, wlwd()) -- Still no window-local directory in this window -- Unless the global change happened in a tab with local directory @@ -101,9 +220,9 @@ for _, cmd in ipairs {'cd', 'chdir'} do -- But not in a window with its own local directory execute('tabnext | wincmd w') - eq(globalDir .. '/' .. directories[2], cwd() ) + eq(globalDir .. '/' .. directories.window, cwd() ) eq(0 , tlwd()) - eq(globalDir .. '/' .. directories[2], wcwd()) + eq(globalDir .. '/' .. directories.window, wcwd()) end) end) end @@ -150,3 +269,21 @@ for _, cmd in ipairs {'getcwd', 'haslocaldir'} do end) end +describe("getcwd()", function () + before_each(function() + clear() + lfs.mkdir(directories.global) + end) + + after_each(function() + helpers.rmdir(directories.global) + end) + + it("returns empty string if working directory does not exist", function() + execute("cd "..directories.global) + execute("call delete('../"..directories.global.."', 'd')") + eq("", helpers.eval("getcwd()")) + end) +end) + + diff --git a/test/functional/ex_cmds/ctrl_c_spec.lua b/test/functional/ex_cmds/ctrl_c_spec.lua new file mode 100644 index 0000000000..072fd2ad10 --- /dev/null +++ b/test/functional/ex_cmds/ctrl_c_spec.lua @@ -0,0 +1,60 @@ +local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') +local clear, feed, source = helpers.clear, helpers.feed, helpers.source +local execute = helpers.execute + +describe("CTRL-C (mapped)", function() + before_each(function() + clear() + end) + + it("interrupts :global", function() + -- Crashes luajit. + if helpers.skip_fragile(pending, + os.getenv("TRAVIS") or os.getenv("APPVEYOR")) then + return + end + + source([[ + set nomore nohlsearch undolevels=-1 + nnoremap <C-C> <NOP> + ]]) + + execute("silent edit! test/functional/fixtures/bigfile.txt") + local screen = Screen.new(52, 6) + screen:attach() + screen:set_default_attr_ids({ + [0] = {foreground = Screen.colors.White, + background = Screen.colors.Red}, + [1] = {bold = true, + foreground = Screen.colors.SeaGreen} + }) + + screen:expect([[ + ^0000;<control>;Cc;0;BN;;;;;N;NULL;;;; | + 0001;<control>;Cc;0;BN;;;;;N;START OF HEADING;;;; | + 0002;<control>;Cc;0;BN;;;;;N;START OF TEXT;;;; | + 0003;<control>;Cc;0;BN;;;;;N;END OF TEXT;;;; | + 0004;<control>;Cc;0;BN;;;;;N;END OF TRANSMISSION;;;;| + | + ]]) + + local function test_ctrl_c(ms) + feed(":global/^/p<CR>") + helpers.sleep(ms) + feed("<C-C>") + screen:expect([[Interrupt]], nil, nil, nil, true) + end + + -- The test is time-sensitive. Try different sleep values. + local ms_values = {1, 10, 100} + for i, ms in ipairs(ms_values) do + if i < #ms_values then + local status, _ = pcall(test_ctrl_c, ms) + if status then break end + else -- Call the last attempt directly. + test_ctrl_c(ms) + end + end + end) +end) diff --git a/test/functional/ex_cmds/dict_notifications_spec.lua b/test/functional/ex_cmds/dict_notifications_spec.lua new file mode 100644 index 0000000000..5e89986c0f --- /dev/null +++ b/test/functional/ex_cmds/dict_notifications_spec.lua @@ -0,0 +1,276 @@ +local helpers = require('test.functional.helpers')(after_each) +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 +local command = helpers.command +local eval = helpers.eval + + +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("does not fail to add/remove if the callback doesn't exist", function() + command('call dictwatcheradd(g:, "key", "g:InvalidCb")') + command('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('does not fail to replace a watcher function', function() + source([[ + function! g:ReplaceWatcher2() + function! g:Watcher2(dict, key, value) + call rpcnotify(g:channel, '2b', a:key, a:value) + endfunction + endfunction + ]]) + command('call g:ReplaceWatcher2()') + command('let g:key = "value"') + eq({'notification', '2b', {'key', {old = 'v2', new = 'value'}}}, next_msg()) + + end) + + it('does not crash when freeing a watched dictionary', function() + source([[ + function! Watcher(dict, key, value) + echo a:key string(a:value) + endfunction + + function! MakeWatch() + let d = {'foo': 'bar'} + call dictwatcheradd(d, 'foo', function('Watcher')) + endfunction + ]]) + + command('call MakeWatch()') + eq(2, eval('1+1')) -- Still alive? + end) + end) +end) diff --git a/test/functional/ex_cmds/drop_spec.lua b/test/functional/ex_cmds/drop_spec.lua new file mode 100644 index 0000000000..99db5ea333 --- /dev/null +++ b/test/functional/ex_cmds/drop_spec.lua @@ -0,0 +1,80 @@ +local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') +local clear, feed, execute = helpers.clear, helpers.feed, helpers.execute + +describe(":drop", function() + local screen + + before_each(function() + clear() + screen = Screen.new(35, 10) + screen:attach() + screen:set_default_attr_ids({ + [0] = {bold=true, foreground=Screen.colors.Blue}, + [1] = {bold = true, reverse = true}, + [2] = {reverse = true}, + [3] = {bold = true}, + }) + execute("set laststatus=2") + end) + + after_each(function() + screen:detach() + end) + + it("works like :e when called with only one window open", function() + execute("drop tmp1.vim") + screen:expect([[ + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {1:tmp1.vim }| + "tmp1.vim" [New File] | + ]]) + end) + + it("switches to an open window showing the buffer", function() + execute("edit tmp1") + execute("vsplit") + execute("edit tmp2") + execute("drop tmp1") + screen:expect([[ + {2:|}^ | + {0:~ }{2:|}{0:~ }| + {0:~ }{2:|}{0:~ }| + {0:~ }{2:|}{0:~ }| + {0:~ }{2:|}{0:~ }| + {0:~ }{2:|}{0:~ }| + {0:~ }{2:|}{0:~ }| + {0:~ }{2:|}{0:~ }| + {2:tmp2 }{1:tmp1 }| + :drop tmp1 | + ]]) + end) + + it("splits off a new window when a buffer can't be abandoned", function() + execute("edit tmp1") + execute("vsplit") + execute("edit tmp2") + feed("iABC<esc>") + execute("drop tmp3") + screen:expect([[ + ^ {2:|} | + {0:~ }{2:|}{0:~ }| + {0:~ }{2:|}{0:~ }| + {0:~ }{2:|}{0:~ }| + {1:tmp3 }{2:|}{0:~ }| + ABC {2:|}{0:~ }| + {0:~ }{2:|}{0:~ }| + {0:~ }{2:|}{0:~ }| + {2:tmp2 [+] tmp1 }| + "tmp3" [New File] | + ]]) + end) + +end) diff --git a/test/functional/ex_cmds/edit_spec.lua b/test/functional/ex_cmds/edit_spec.lua new file mode 100644 index 0000000000..3cc5f5fb95 --- /dev/null +++ b/test/functional/ex_cmds/edit_spec.lua @@ -0,0 +1,26 @@ +local helpers = require("test.functional.helpers")(after_each) +local eq, execute, funcs = helpers.eq, helpers.execute, helpers.funcs +local ok = helpers.ok +local clear = helpers.clear + +describe(":edit", function() + before_each(function() + clear() + end) + + it("without arguments does not restart :terminal buffer", function() + execute("terminal") + helpers.feed([[<C-\><C-N>]]) + local bufname_before = funcs.bufname("%") + local bufnr_before = funcs.bufnr("%") + helpers.ok(nil ~= string.find(bufname_before, "^term://")) -- sanity + + execute("edit") + + local bufname_after = funcs.bufname("%") + local bufnr_after = funcs.bufnr("%") + ok(funcs.line('$') > 1) + eq(bufname_before, bufname_after) + eq(bufnr_before, bufnr_after) + end) +end) diff --git a/test/functional/ex_cmds/encoding_spec.lua b/test/functional/ex_cmds/encoding_spec.lua index 6d402b7974..87ed7a2d0a 100644 --- a/test/functional/ex_cmds/encoding_spec.lua +++ b/test/functional/ex_cmds/encoding_spec.lua @@ -1,4 +1,4 @@ -local helpers = require('test.functional.helpers') +local helpers = require('test.functional.helpers')(after_each) local clear, execute, feed = helpers.clear, helpers.execute, helpers.feed local eq, neq, eval = helpers.eq, helpers.neq, helpers.eval @@ -15,27 +15,26 @@ describe('&encoding', function() execute('set encoding=latin1') -- error message expected feed('<cr>') - neq(nil, string.find(eval('v:errmsg'), '^E905:')) + neq(nil, string.find(eval('v:errmsg'), '^E474:')) eq('utf-8', eval('&encoding')) -- check nvim is still in utf-8 mode eq(3, eval('strwidth("Bär")')) end) - it('can be changed before startup', function() - clear('set enc=latin1') - execute('set encoding=utf-8') + it('cannot be changed before startup', function() + clear('--cmd', 'set enc=latin1') -- error message expected feed('<cr>') - eq('latin1', eval('&encoding')) - eq(4, eval('strwidth("Bär")')) + neq(nil, string.find(eval('v:errmsg'), '^E474:')) + eq('utf-8', eval('&encoding')) + eq(3, eval('strwidth("Bär")')) end) - it('is not changed by `set all&`', function() - -- we need to set &encoding to something non-default. Use 'latin1' - clear('set enc=latin1') - execute('set all&') - eq('latin1', eval('&encoding')) - eq(4, eval('strwidth("Bär")')) - end) + it('can be set to utf-8 without error', function() + execute('set encoding=utf-8') + eq("", eval('v:errmsg')) + clear('--cmd', 'set enc=utf-8') + eq("", eval('v:errmsg')) + end) end) diff --git a/test/functional/ex_cmds/grep_spec.lua b/test/functional/ex_cmds/grep_spec.lua index f3ff0a3817..13f88b7e03 100644 --- a/test/functional/ex_cmds/grep_spec.lua +++ b/test/functional/ex_cmds/grep_spec.lua @@ -1,4 +1,4 @@ -local helpers = require('test.functional.helpers') +local helpers = require('test.functional.helpers')(after_each) local clear, execute, feed, ok, eval = helpers.clear, helpers.execute, helpers.feed, helpers.ok, helpers.eval diff --git a/test/functional/ex_cmds/menu_spec.lua b/test/functional/ex_cmds/menu_spec.lua index f5fd30465d..52df9e1592 100644 --- a/test/functional/ex_cmds/menu_spec.lua +++ b/test/functional/ex_cmds/menu_spec.lua @@ -1,4 +1,4 @@ -local helpers = require('test.functional.helpers') +local helpers = require('test.functional.helpers')(after_each) local clear, execute, nvim = helpers.clear, helpers.execute, helpers.nvim local expect, feed, command = helpers.expect, helpers.feed, helpers.command local eq, eval = helpers.eq, helpers.eval @@ -39,7 +39,7 @@ describe(':emenu', function() end) it('executes correct bindings in command mode', function() - feed('ithis is a sentence<esc>^"+yiwo<esc>') + feed('ithis is a sentence<esc>^yiwo<esc>') -- Invoke "Edit.Paste" in normal-mode. nvim('command', 'emenu Edit.Paste') diff --git a/test/functional/ex_cmds/oldfiles_spec.lua b/test/functional/ex_cmds/oldfiles_spec.lua index 5bba1a0e7c..a218bb6633 100644 --- a/test/functional/ex_cmds/oldfiles_spec.lua +++ b/test/functional/ex_cmds/oldfiles_spec.lua @@ -1,5 +1,5 @@ local Screen = require('test.functional.ui.screen') -local helpers = require('test.functional.helpers') +local helpers = require('test.functional.helpers')(after_each) local buf, eq, execute = helpers.curbufmeths, helpers.eq, helpers.execute local feed, nvim_prog, wait = helpers.feed, helpers.nvim_prog, helpers.wait @@ -47,7 +47,7 @@ describe(':oldfiles', function() end) end) -describe(':oldfiles!', function() +describe(':browse oldfiles', function() local filename local filename2 local oldfiles @@ -65,7 +65,7 @@ describe(':oldfiles!', function() -- Ensure nvim is out of "Press ENTER..." screen feed('<cr>') - + -- 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. @@ -74,7 +74,7 @@ describe(':oldfiles!', function() ok(filename == oldfiles[1] or filename == oldfiles[2]) ok(filename2 == oldfiles[1] or filename2 == oldfiles[2]) - execute('oldfiles!') + execute('browse oldfiles') end) after_each(function() diff --git a/test/functional/ex_cmds/profile_spec.lua b/test/functional/ex_cmds/profile_spec.lua index 744b22621f..f185db192a 100644 --- a/test/functional/ex_cmds/profile_spec.lua +++ b/test/functional/ex_cmds/profile_spec.lua @@ -1,13 +1,13 @@ require('os') local lfs = require('lfs') -local helpers = require('test.functional.helpers') +local helpers = require('test.functional.helpers')(after_each) local eval = helpers.eval local command = helpers.command local eq, neq = helpers.eq, helpers.neq -local tempfile = os.tmpname() +local tempfile = helpers.tmpname() --- os.tmpname() also creates the file on POSIX systems. Remove it again. +-- 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) diff --git a/test/functional/ex_cmds/quit_spec.lua b/test/functional/ex_cmds/quit_spec.lua index a8156228d3..fe138a24c8 100644 --- a/test/functional/ex_cmds/quit_spec.lua +++ b/test/functional/ex_cmds/quit_spec.lua @@ -1,9 +1,9 @@ -local helpers = require('test.functional.helpers') +local helpers = require('test.functional.helpers')(after_each) local clear = helpers.clear describe(':qa', function() - before_each(function() - clear('qa') + before_each(function() + clear('--cmd', 'qa') end) it('verify #3334', function() diff --git a/test/functional/ex_cmds/recover_spec.lua b/test/functional/ex_cmds/recover_spec.lua index e1d01f6896..af1296c94c 100644 --- a/test/functional/ex_cmds/recover_spec.lua +++ b/test/functional/ex_cmds/recover_spec.lua @@ -1,11 +1,13 @@ -- Tests for :recover -local helpers = require('test.functional.helpers') +local helpers = require('test.functional.helpers')(after_each) local lfs = require('lfs') local execute, eq, clear, eval, feed, expect, source = helpers.execute, helpers.eq, helpers.clear, helpers.eval, helpers.feed, helpers.expect, helpers.source +if helpers.pending_win32(pending) then return end + describe(':recover', function() before_each(clear) @@ -30,7 +32,7 @@ describe(':preserve', function() it("saves to custom 'directory' and (R)ecovers (issue #1836)", function() local testfile = 'testfile_recover_spec' - -- Note: `set swapfile` *must* go after `set directory`: otherwise it may + -- Note: `set swapfile` *must* go after `set directory`: otherwise it may -- attempt to create a swapfile in different directory. local init = [[ set directory^=]]..swapdir..[[// diff --git a/test/functional/ex_cmds/sign_spec.lua b/test/functional/ex_cmds/sign_spec.lua index c50704504d..b37e6e8563 100644 --- a/test/functional/ex_cmds/sign_spec.lua +++ b/test/functional/ex_cmds/sign_spec.lua @@ -1,4 +1,4 @@ -local helpers = require('test.functional.helpers') +local helpers = require('test.functional.helpers')(after_each) local clear, nvim, eq = helpers.clear, helpers.nvim, helpers.eq describe('sign', function() diff --git a/test/functional/ex_cmds/undojoin_spec.lua b/test/functional/ex_cmds/undojoin_spec.lua new file mode 100644 index 0000000000..ba1e46ceb3 --- /dev/null +++ b/test/functional/ex_cmds/undojoin_spec.lua @@ -0,0 +1,38 @@ +local helpers = require('test.functional.helpers')(after_each) + +local eq = helpers.eq +local clear = helpers.clear +local insert = helpers.insert +local feed = helpers.feed +local expect = helpers.expect +local execute = helpers.execute +local exc_exec = helpers.exc_exec + +describe(':undojoin command', function() + before_each(function() + clear() + insert([[ + Line of text 1 + Line of text 2]]) + execute('goto 1') + end) + it('joins changes in a buffer', function() + execute('undojoin | delete') + expect([[ + Line of text 2]]) + feed('u') + expect([[ + ]]) + end) + it('does not corrupt undolist when connected with redo', function() + feed('ixx<esc>') + execute('undojoin | redo') + expect([[ + xxLine of text 1 + Line of text 2]]) + end) + it('does not raise an error when called twice', function() + local ret = exc_exec('undojoin | undojoin') + eq(0, ret) + end) +end) diff --git a/test/functional/ex_cmds/write_spec.lua b/test/functional/ex_cmds/write_spec.lua index d90b297ca8..4ac9f312ef 100644 --- a/test/functional/ex_cmds/write_spec.lua +++ b/test/functional/ex_cmds/write_spec.lua @@ -1,15 +1,26 @@ --- Specs for :write +local helpers = require('test.functional.helpers')(after_each) +local eq, eval, clear, write_file, execute, source, insert = + helpers.eq, helpers.eval, helpers.clear, helpers.write_file, + helpers.execute, helpers.source, helpers.insert -local helpers = require('test.functional.helpers') -local eq, eval, clear, write_file, execute, source = - helpers.eq, helpers.eval, helpers.clear, helpers.write_file, - helpers.execute, helpers.source +if helpers.pending_win32(pending) then return end describe(':write', function() - it('&backupcopy=auto preserves symlinks', function() - clear('set backupcopy=auto') + local function cleanup() os.remove('test_bkc_file.txt') os.remove('test_bkc_link.txt') + os.remove('test_fifo') + end + before_each(function() + clear() + cleanup() + end) + after_each(function() + cleanup() + end) + + it('&backupcopy=auto preserves symlinks', function() + execute('set backupcopy=auto') write_file('test_bkc_file.txt', 'content0') execute("silent !ln -s test_bkc_file.txt test_bkc_link.txt") source([[ @@ -22,9 +33,7 @@ describe(':write', function() end) it('&backupcopy=no replaces symlink with new file', function() - clear('set backupcopy=no') - os.remove('test_bkc_file.txt') - os.remove('test_bkc_link.txt') + execute('set backupcopy=no') write_file('test_bkc_file.txt', 'content0') execute("silent !ln -s test_bkc_file.txt test_bkc_link.txt") source([[ @@ -35,4 +44,23 @@ describe(':write', function() eq(eval("['content0']"), eval("readfile('test_bkc_file.txt')")) eq(eval("['content1']"), eval("readfile('test_bkc_link.txt')")) end) + + it("appends FIFO file", function() + if eval("executable('mkfifo')") == 0 then + pending('missing "mkfifo" command', function()end) + return + end + + local text = "some fifo text from write_spec" + assert(os.execute("mkfifo test_fifo")) + insert(text) + + -- Blocks until a consumer reads the FIFO. + execute("write >> test_fifo") + + -- Read the FIFO, this will unblock the :write above. + local fifo = assert(io.open("test_fifo")) + eq(text.."\n", fifo:read("*all")) + fifo:close() + end) end) diff --git a/test/functional/ex_cmds/wundo_spec.lua b/test/functional/ex_cmds/wundo_spec.lua index 969dfea3d9..e1216fa5d4 100644 --- a/test/functional/ex_cmds/wundo_spec.lua +++ b/test/functional/ex_cmds/wundo_spec.lua @@ -1,6 +1,6 @@ -- Specs for :wundo and underlying functions -local helpers = require('test.functional.helpers') +local helpers = require('test.functional.helpers')(after_each) 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 diff --git a/test/functional/ex_cmds/wviminfo_spec.lua b/test/functional/ex_cmds/wviminfo_spec.lua index 21f14be62c..37f45da2d4 100644 --- a/test/functional/ex_cmds/wviminfo_spec.lua +++ b/test/functional/ex_cmds/wviminfo_spec.lua @@ -1,4 +1,5 @@ -local helpers, lfs = require('test.functional.helpers'), require('lfs') +local helpers = require('test.functional.helpers')(after_each) +local lfs = require('lfs') local execute, eq, neq, spawn, nvim_prog, set_session, wait, write_file = helpers.execute, helpers.eq, helpers.neq, helpers.spawn, helpers.nvim_prog, helpers.set_session, helpers.wait, helpers.write_file |