diff options
Diffstat (limited to 'test/functional')
173 files changed, 14776 insertions, 887 deletions
diff --git a/test/functional/api/buffer_spec.lua b/test/functional/api/buffer_spec.lua index c924988d06..0eefa25a13 100644 --- a/test/functional/api/buffer_spec.lua +++ b/test/functional/api/buffer_spec.lua @@ -1,8 +1,9 @@ -- Sanity checks for buffer_* API calls via msgpack-rpc local helpers = require('test.functional.helpers') -local clear, nvim, buffer, curbuf, curwin, eq, ok = - helpers.clear, helpers.nvim, helpers.buffer, helpers.curbuf, helpers.curwin, - helpers.eq, helpers.ok +local clear, nvim, buffer = helpers.clear, helpers.nvim, helpers.buffer +local curbuf, curwin, eq = helpers.curbuf, helpers.curwin, helpers.eq +local curbufmeths, ok = helpers.curbufmeths, helpers.ok +local funcs = helpers.funcs describe('buffer_* functions', function() before_each(clear) @@ -35,10 +36,11 @@ describe('buffer_* functions', function() eq('', curbuf('get_line', 0)) end) - it('get_line: out-of-bounds returns empty string', function() + it('get_line: out-of-bounds is an error', function() curbuf('set_line', 0, 'line1.a') - eq('', curbuf('get_line', 1)) - eq('', curbuf('get_line', -2)) + eq(1, curbuf('line_count')) -- sanity + eq(false, pcall(curbuf, 'get_line', 1)) + eq(false, pcall(curbuf, 'get_line', -2)) end) it('set_line, del_line: out-of-bounds is an error', function() @@ -68,14 +70,16 @@ describe('buffer_* functions', function() eq({}, curbuf('get_line_slice', -4, -5, true, true)) end) - it('set_line_slice: out-of-bounds is an error', function() + it('set_line_slice: out-of-bounds extends past end', function() curbuf('set_line_slice', 0, 0, true, true, {'a', 'b', 'c'}) eq({'a', 'b', 'c'}, curbuf('get_line_slice', 0, 2, true, true)) --sanity eq({'c'}, curbuf('get_line_slice', -1, 4, true, true)) eq({'a', 'b', 'c'}, curbuf('get_line_slice', 0, 5, true, true)) - eq(false, pcall(curbuf, 'set_line_slice', 4, 5, true, true, {'d'})) - eq(false, pcall(curbuf, 'set_line_slice', -4, -5, true, true, {'d'})) + curbuf('set_line_slice', 4, 5, true, true, {'d'}) + eq({'a', 'b', 'c', 'd'}, curbuf('get_line_slice', 0, 5, true, true)) + curbuf('set_line_slice', -4, -5, true, true, {'e'}) + eq({'e', 'a', 'b', 'c', 'd'}, curbuf('get_line_slice', 0, 5, true, true)) end) it('works', function() @@ -101,11 +105,144 @@ describe('buffer_* functions', function() end) end) - describe('{get,set}_var', function() + describe('{get,set}_lines', function() + local get_lines, set_lines = curbufmeths.get_lines, curbufmeths.set_lines + local line_count = curbufmeths.line_count + + it('has correct line_count when inserting and deleting', function() + eq(1, line_count()) + set_lines(-1, -1, true, {'line'}) + eq(2, line_count()) + set_lines(-1, -1, true, {'line'}) + eq(3, line_count()) + set_lines(-2, -1, true, {}) + eq(2, line_count()) + set_lines(-2, -1, true, {}) + set_lines(-2, -1, true, {}) + -- There's always at least one line + eq(1, line_count()) + end) + + it('can get, set and delete a single line', function() + eq({''}, get_lines(0, 1, true)) + set_lines(0, 1, true, {'line1'}) + eq({'line1'}, get_lines(0, 1, true)) + set_lines(0, 1, true, {'line2'}) + eq({'line2'}, get_lines(0, 1, true)) + set_lines(0, 1, true, {}) + eq({''}, get_lines(0, 1, true)) + end) + + it('can get a single line with strict indexing', function() + set_lines(0, 1, true, {'line1.a'}) + eq(1, line_count()) -- sanity + eq(false, pcall(get_lines, 1, 2, true)) + eq(false, pcall(get_lines, -3, -2, true)) + end) + + it('can get a single line with non-strict indexing', function() + set_lines(0, 1, true, {'line1.a'}) + eq(1, line_count()) -- sanity + eq({}, get_lines(1, 2, false)) + eq({}, get_lines(-3, -2, false)) + end) + + it('can set and delete a single line with strict indexing', function() + set_lines(0, 1, true, {'line1.a'}) + eq(false, pcall(set_lines, 1, 2, true, {'line1.b'})) + eq(false, pcall(set_lines, -3, -2, true, {'line1.c'})) + eq({'line1.a'}, get_lines(0, -1, true)) + eq(false, pcall(set_lines, 1, 2, true, {})) + eq(false, pcall(set_lines, -3, -2, true, {})) + eq({'line1.a'}, get_lines(0, -1, true)) + end) + + it('can set and delete a single line with non-strict indexing', function() + set_lines(0, 1, true, {'line1.a'}) + set_lines(1, 2, false, {'line1.b'}) + set_lines(-4, -3, false, {'line1.c'}) + eq({'line1.c', 'line1.a', 'line1.b'}, get_lines(0, -1, true)) + set_lines(3, 4, false, {}) + set_lines(-5, -4, false, {}) + eq({'line1.c', 'line1.a', 'line1.b'}, get_lines(0, -1, true)) + end) + + it('can handle NULs', function() + set_lines(0, 1, true, {'ab\0cd'}) + eq({'ab\0cd'}, get_lines(0, -1, true)) + end) + + it('works with multiple lines', function() + eq({''}, get_lines(0, -1, true)) + -- Replace buffer + for _, mode in pairs({false, true}) do + set_lines(0, -1, mode, {'a', 'b', 'c'}) + eq({'a', 'b', 'c'}, get_lines(0, -1, mode)) + eq({'b', 'c'}, get_lines(1, -1, mode)) + eq({'b'}, get_lines(1, 2, mode)) + eq({}, get_lines(1, 1, mode)) + eq({'a', 'b'}, get_lines(0, -2, mode)) + eq({'b'}, get_lines(1, -2, mode)) + eq({'b', 'c'}, get_lines(-3, -1, mode)) + set_lines(1, 2, mode, {'a', 'b', 'c'}) + eq({'a', 'a', 'b', 'c', 'c'}, get_lines(0, -1, mode)) + set_lines(-2, -1, mode, {'a', 'b', 'c'}) + eq({'a', 'a', 'b', 'c', 'a', 'b', 'c'}, + get_lines(0, -1, mode)) + set_lines(0, -4, mode, {}) + eq({'a', 'b', 'c'}, get_lines(0, -1, mode)) + set_lines(0, -1, mode, {}) + eq({''}, get_lines(0, -1, mode)) + end + end) + + it('can get line ranges with non-strict indexing', function() + set_lines(0, -1, true, {'a', 'b', 'c'}) + eq({'a', 'b', 'c'}, get_lines(0, -1, true)) --sanity + + eq({}, get_lines(3, 4, false)) + eq({}, get_lines(3, 10, false)) + eq({}, get_lines(-5, -5, false)) + eq({}, get_lines(3, -1, false)) + eq({}, get_lines(-3, -4, false)) + end) + + it('can get line ranges with strict indexing', function() + set_lines(0, -1, true, {'a', 'b', 'c'}) + eq({'a', 'b', 'c'}, get_lines(0, -1, true)) --sanity + + eq(false, pcall(get_lines, 3, 4, true)) + eq(false, pcall(get_lines, 3, 10, true)) + eq(false, pcall(get_lines, -5, -5, true)) + -- empty or inverted ranges are not errors + eq({}, get_lines(3, -1, true)) + eq({}, get_lines(-3, -4, true)) + end) + + it('set_line_slice: out-of-bounds can extend past end', function() + set_lines(0, -1, true, {'a', 'b', 'c'}) + eq({'a', 'b', 'c'}, get_lines(0, -1, true)) --sanity + + eq({'c'}, get_lines(-2, 5, false)) + eq({'a', 'b', 'c'}, get_lines(0, 6, false)) + eq(false, pcall(set_lines, 4, 6, true, {'d'})) + set_lines(4, 6, false, {'d'}) + eq({'a', 'b', 'c', 'd'}, get_lines(0, -1, true)) + eq(false, pcall(set_lines, -6, -6, true, {'e'})) + set_lines(-6, -6, false, {'e'}) + eq({'e', 'a', 'b', 'c', 'd'}, get_lines(0, -1, true)) + end) + + end) + + describe('{get,set,del}_var', function() it('works', function() curbuf('set_var', 'lua', {1, 2, {['3'] = 1}}) eq({1, 2, {['3'] = 1}}, curbuf('get_var', 'lua')) eq({1, 2, {['3'] = 1}}, nvim('eval', 'b:lua')) + eq(1, funcs.exists('b:lua')) + curbufmeths.del_var('lua') + eq(0, funcs.exists('b:lua')) end) end) diff --git a/test/functional/api/menu_spec.lua b/test/functional/api/menu_spec.lua index 34d23ca098..5b414fb559 100644 --- a/test/functional/api/menu_spec.lua +++ b/test/functional/api/menu_spec.lua @@ -19,7 +19,7 @@ describe("update_menu notification", function() screen:detach() end) - function expect_sent(expected) + local function expect_sent(expected) screen:wait(function() if screen.update_menu ~= expected then if expected then diff --git a/test/functional/api/server_requests_spec.lua b/test/functional/api/server_requests_spec.lua index 4c7122a549..1b33275803 100644 --- a/test/functional/api/server_requests_spec.lua +++ b/test/functional/api/server_requests_spec.lua @@ -32,6 +32,26 @@ describe('server -> client', function() end) end) + describe('empty string handling in arrays', function() + -- Because the msgpack encoding for an empty string was interpreted as an + -- error, msgpack arrays with an empty string looked like + -- [..., '', 0, ..., 0] after the conversion, regardless of the array + -- elements following the empty string. + it('works', function() + local function on_setup() + eq({1, 2, '', 3, 'asdf'}, eval('rpcrequest('..cid..', "nstring")')) + stop() + end + + local function on_request() + -- No need to evaluate the args, we are only interested in + -- a response that contains an array with an empty string. + return {1, 2, '', 3, 'asdf'} + end + run(on_request, nil, on_setup) + end) + end) + describe('recursive call', function() it('works', function() local function on_setup() @@ -95,13 +115,13 @@ describe('server -> client', function() eq('notified!', eval('rpcrequest('..cid..', "notify")')) end - local function on_request(method, args) + local function on_request(method) eq('notify', method) eq(1, eval('rpcnotify('..cid..', "notification")')) return 'notified!' end - local function on_notification(method, args) + local function on_notification(method) eq('notification', method) if notified == expected then stop() @@ -145,8 +165,8 @@ describe('server -> client', function() eq('SOME TEXT', eval("rpcrequest(vim, 'buffer_get_line', "..buf..", 0)")) - -- Call get_line_slice(buf, range [0,0], includes start, includes end) - eq({'SOME TEXT'}, eval("rpcrequest(vim, 'buffer_get_line_slice', "..buf..", 0, 0, 1, 1)")) + -- Call get_lines(buf, range [0,0], strict_indexing) + eq({'SOME TEXT'}, eval("rpcrequest(vim, 'buffer_get_lines', "..buf..", 0, 1, 1)")) end) it('returns an error if the request failed', function() diff --git a/test/functional/api/tabpage_spec.lua b/test/functional/api/tabpage_spec.lua index 9937e0c72e..c782107714 100644 --- a/test/functional/api/tabpage_spec.lua +++ b/test/functional/api/tabpage_spec.lua @@ -3,6 +3,8 @@ local helpers = require('test.functional.helpers') local clear, nvim, tabpage, curtab, eq, ok = helpers.clear, helpers.nvim, helpers.tabpage, helpers.curtab, helpers.eq, helpers.ok +local curtabmeths = helpers.curtabmeths +local funcs = helpers.funcs describe('tabpage_* functions', function() before_each(clear) @@ -21,11 +23,14 @@ describe('tabpage_* functions', function() end) end) - describe('{get,set}_var', function() + describe('{get,set,del}_var', function() it('works', function() curtab('set_var', 'lua', {1, 2, {['3'] = 1}}) eq({1, 2, {['3'] = 1}}, curtab('get_var', 'lua')) eq({1, 2, {['3'] = 1}}, nvim('eval', 't:lua')) + eq(1, funcs.exists('t:lua')) + curtabmeths.del_var('lua') + eq(0, funcs.exists('t:lua')) end) end) diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index eb4804f141..20de6d0072 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -1,9 +1,12 @@ -- Sanity checks for vim_* API calls via msgpack-rpc local helpers = require('test.functional.helpers') local Screen = require('test.functional.ui.screen') +local NIL = helpers.NIL local clear, nvim, eq, neq = helpers.clear, helpers.nvim, helpers.eq, helpers.neq local ok, nvim_async, feed = helpers.ok, helpers.nvim_async, helpers.feed - +local os_name = helpers.os_name +local meths = helpers.meths +local funcs = helpers.funcs describe('vim_* functions', function() before_each(clear) @@ -17,7 +20,11 @@ describe('vim_* functions', function() nvim('command', 'w') local f = io.open(fname) ok(f ~= nil) - eq('testing\napi\n', f:read('*a')) + if os_name() == 'windows' then + eq('testing\r\napi\r\n', f:read('*a')) + else + eq('testing\napi\n', f:read('*a')) + end f:close() os.remove(fname) end) @@ -66,20 +73,31 @@ describe('vim_* functions', function() end) end) - describe('{get,set}_var', function() + describe('{get,set,del}_var', function() it('works', function() nvim('set_var', 'lua', {1, 2, {['3'] = 1}}) eq({1, 2, {['3'] = 1}}, nvim('get_var', 'lua')) eq({1, 2, {['3'] = 1}}, nvim('eval', 'g:lua')) + eq(1, funcs.exists('g:lua')) + meths.del_var('lua') + eq(0, funcs.exists('g:lua')) end) it('set_var returns the old value', function() local val1 = {1, 2, {['3'] = 1}} local val2 = {4, 7} - eq(nil, nvim('set_var', 'lua', val1)) + eq(NIL, nvim('set_var', 'lua', val1)) eq(val1, nvim('set_var', 'lua', val2)) end) + it('del_var returns the old value', function() + local val1 = {1, 2, {['3'] = 1}} + local val2 = {4, 7} + eq(NIL, meths.set_var('lua', val1)) + eq(val1, meths.set_var('lua', val2)) + eq(val2, meths.del_var('lua')) + end) + it('truncates values with NULs in them', function() nvim('set_var', 'xxx', 'ab\0cd') eq('ab', nvim('get_var', 'xxx')) @@ -140,15 +158,23 @@ describe('vim_* functions', function() describe('replace_termcodes', function() it('escapes K_SPECIAL as K_SPECIAL KS_SPECIAL KE_FILLER', function() - eq(helpers.nvim('replace_termcodes', '\128', true, true, true), '\128\254X') + eq('\128\254X', helpers.nvim('replace_termcodes', '\128', true, true, true)) end) - it('leaves non K_SPECIAL string unchanged', function() - eq(helpers.nvim('replace_termcodes', 'abc', true, true, true), 'abc') + it('leaves non-K_SPECIAL string unchanged', function() + eq('abc', helpers.nvim('replace_termcodes', 'abc', true, true, true)) end) it('converts <expressions>', function() - eq(helpers.nvim('replace_termcodes', '<Leader>', true, true, true), '\\') + eq('\\', helpers.nvim('replace_termcodes', '<Leader>', true, true, true)) + end) + + it('converts <LeftMouse> to K_SPECIAL KS_EXTRA KE_LEFTMOUSE', function() + -- K_SPECIAL KS_EXTRA KE_LEFTMOUSE + -- 0x80 0xfd 0x2c + -- 128 253 44 + eq('\128\253\44', helpers.nvim('replace_termcodes', + '<LeftMouse>', true, true, true)) end) end) @@ -180,6 +206,8 @@ describe('vim_* functions', function() end) describe('err_write', function() + local screen + before_each(function() clear() screen = Screen.new(40, 8) diff --git a/test/functional/api/window_spec.lua b/test/functional/api/window_spec.lua index 456252522d..92a33b4cdb 100644 --- a/test/functional/api/window_spec.lua +++ b/test/functional/api/window_spec.lua @@ -1,10 +1,12 @@ -- Sanity checks for window_* API calls via msgpack-rpc local helpers = require('test.functional.helpers') -local clear, nvim, buffer, curbuf, curbuf_contents, window, curwin, eq, neq, - ok, feed, rawfeed, insert, eval = helpers.clear, helpers.nvim, helpers.buffer, helpers.curbuf, +local clear, nvim, curbuf, curbuf_contents, window, curwin, eq, neq, + ok, feed, insert, eval = helpers.clear, helpers.nvim, helpers.curbuf, helpers.curbuf_contents, helpers.window, helpers.curwin, helpers.eq, - helpers.neq, helpers.ok, helpers.feed, helpers.rawfeed, helpers.insert, helpers.eval + helpers.neq, helpers.ok, helpers.feed, helpers.insert, helpers.eval local wait = helpers.wait +local curwinmeths = helpers.curwinmeths +local funcs = helpers.funcs -- check if str is visible at the beginning of some line local function is_visible(str) @@ -54,7 +56,7 @@ describe('window_* functions', function() insert("prologue") feed('100o<esc>') insert("epilogue") - win = curwin() + local win = curwin() feed('gg') wait() -- let nvim process the 'gg' command @@ -126,11 +128,14 @@ describe('window_* functions', function() end) end) - describe('{get,set}_var', function() + describe('{get,set,del}_var', function() it('works', function() curwin('set_var', 'lua', {1, 2, {['3'] = 1}}) eq({1, 2, {['3'] = 1}}, curwin('get_var', 'lua')) eq({1, 2, {['3'] = 1}}, nvim('eval', 'w:lua')) + eq(1, funcs.exists('w:lua')) + curwinmeths.del_var('lua') + eq(0, funcs.exists('w:lua')) end) end) diff --git a/test/functional/autocmd/autocmd_spec.lua b/test/functional/autocmd/autocmd_spec.lua new file mode 100644 index 0000000000..3c813abc2e --- /dev/null +++ b/test/functional/autocmd/autocmd_spec.lua @@ -0,0 +1,31 @@ +local helpers = require('test.functional.helpers') + +local clear = helpers.clear +local command = helpers.command +local eval = helpers.eval + +describe('autocmds:', function() + before_each(clear) + + it(':tabnew triggers events in the correct order', function() + local expected = { + 'WinLeave', + 'TabLeave', + 'TabNew', + 'WinEnter', + 'TabEnter', + 'BufLeave', + 'BufEnter' + } + command('let g:foo = []') + command('autocmd BufEnter * :call add(g:foo, "BufEnter")') + command('autocmd BufLeave * :call add(g:foo, "BufLeave")') + command('autocmd TabEnter * :call add(g:foo, "TabEnter")') + command('autocmd TabLeave * :call add(g:foo, "TabLeave")') + command('autocmd TabNew * :call add(g:foo, "TabNew")') + command('autocmd WinEnter * :call add(g:foo, "WinEnter")') + command('autocmd WinLeave * :call add(g:foo, "WinLeave")') + command('tabnew') + assert.same(expected, eval('g:foo')) + end) +end) diff --git a/test/functional/autocmd/tabclose_spec.lua b/test/functional/autocmd/tabclose_spec.lua index 847362e3de..bf609d1846 100644 --- a/test/functional/autocmd/tabclose_spec.lua +++ b/test/functional/autocmd/tabclose_spec.lua @@ -1,7 +1,5 @@ local helpers = require('test.functional.helpers') -local clear, nvim, buffer, curbuf, curwin, eq, neq, ok = - helpers.clear, helpers.nvim, helpers.buffer, helpers.curbuf, helpers.curwin, - helpers.eq, helpers.neq, helpers.ok +local clear, nvim, eq = helpers.clear, helpers.nvim, helpers.eq describe('TabClosed', function() describe('au TabClosed', function() @@ -20,7 +18,6 @@ describe('TabClosed', function() end) describe('with NR as <afile>', function() it('matches when closing a tab whose index is NR', function() - tmp_path = nvim('eval', 'tempname()') nvim('command', 'au! TabClosed 2 echom "tabclosed:match"') repeat nvim('command', 'tabnew') diff --git a/test/functional/autocmd/tabnew_spec.lua b/test/functional/autocmd/tabnew_spec.lua index d80644cd92..aaf9db0a99 100644 --- a/test/functional/autocmd/tabnew_spec.lua +++ b/test/functional/autocmd/tabnew_spec.lua @@ -1,24 +1,28 @@ local helpers = require('test.functional.helpers') -local clear, nvim, buffer, curbuf, curwin, eq, neq, ok = - helpers.clear, helpers.nvim, helpers.buffer, helpers.curbuf, helpers.curwin, - helpers.eq, helpers.neq, helpers.ok -describe('TabNew', function() - setup(clear) - describe('au TabNew', function() - describe('with * as <afile>', function() - it('matches when opening any new tab', function() - nvim('command', 'au! TabNew * echom "tabnew:".tabpagenr().":".bufnr("")') - eq("\ntabnew:2:1", nvim('command_output', 'tabnew')) - eq("\ntabnew:3:2\n\"test.x\" [New File]", nvim('command_output', 'tabnew test.x')) - end) - end) - describe('with FILE as <afile>', function() - it('matches when opening a new tab for FILE', function() - tmp_path = nvim('eval', 'tempname()') - nvim('command', 'au! TabNew '..tmp_path..' echom "tabnew:match"') - eq("\ntabnew:4:3\ntabnew:match\n\""..tmp_path.."\" [New File]", nvim('command_output', 'tabnew '..tmp_path)) - end) - end) - end) +local clear = helpers.clear +local command = helpers.command +local eq = helpers.eq +local eval = helpers.eval + +describe('autocmd TabNew', function() + before_each(clear) + + it('matches when opening any new tab', function() + command('autocmd! TabNew * let g:test = "tabnew:".tabpagenr().":".bufnr("")') + command('tabnew') + eq('tabnew:2:1', eval('g:test')) + command('tabnew test.x') + eq('tabnew:3:2', eval('g:test')) + end) + + it('matches when opening a new tab for FILE', function() + local tmp_path = helpers.funcs.tempname() + command('let g:test = "foo"') + command('autocmd! TabNew ' .. tmp_path .. ' let g:test = "bar"') + command('tabnew ' .. tmp_path ..'X') + eq('foo', eval('g:test')) + command('tabnew ' .. tmp_path) + eq('bar', eval('g:test')) + end) end) diff --git a/test/functional/autocmd/tabnewentered_spec.lua b/test/functional/autocmd/tabnewentered_spec.lua index f220c15ef7..64b9a22f41 100644 --- a/test/functional/autocmd/tabnewentered_spec.lua +++ b/test/functional/autocmd/tabnewentered_spec.lua @@ -1,7 +1,5 @@ local helpers = require('test.functional.helpers') -local clear, nvim, buffer, curbuf, curwin, eq, neq, ok = - helpers.clear, helpers.nvim, helpers.buffer, helpers.curbuf, helpers.curwin, - helpers.eq, helpers.neq, helpers.ok +local clear, nvim, eq = helpers.clear, helpers.nvim, helpers.eq describe('TabNewEntered', function() describe('au TabNewEntered', function() @@ -15,7 +13,7 @@ describe('TabNewEntered', function() end) describe('with FILE as <afile>', function() it('matches when opening a new tab for FILE', function() - tmp_path = nvim('eval', 'tempname()') + local tmp_path = nvim('eval', 'tempname()') nvim('command', 'au! TabNewEntered '..tmp_path..' echom "tabnewentered:match"') eq("\n\""..tmp_path.."\" [New File]\ntabnewentered:4:4\ntabnewentered:match", nvim('command_output', 'tabnew '..tmp_path)) end) diff --git a/test/functional/autocmd/termclose_spec.lua b/test/functional/autocmd/termclose_spec.lua new file mode 100644 index 0000000000..4de3f039c1 --- /dev/null +++ b/test/functional/autocmd/termclose_spec.lua @@ -0,0 +1,44 @@ +local helpers = require('test.functional.helpers') +local Screen = require('test.functional.ui.screen') + +local clear, execute, feed, nvim, nvim_dir = helpers.clear, +helpers.execute, helpers.feed, helpers.nvim, helpers.nvim_dir +local eval, eq = helpers.eval, helpers.eq + +describe('TermClose event', function() + local screen + 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) + + it('reports the correct <abuf>', function() + execute('set hidden') + execute('autocmd TermClose * let g:abuf = expand("<abuf>")') + execute('edit foo') + execute('edit bar') + eq(2, eval('bufnr("%")')) + execute('terminal') + feed('<c-\\><c-n>') + eq(3, eval('bufnr("%")')) + execute('buffer 1') + eq(1, eval('bufnr("%")')) + execute('3bdelete!') + eq('3', eval('g:abuf')) + end) +end) diff --git a/test/functional/autocmd/textyankpost_spec.lua b/test/functional/autocmd/textyankpost_spec.lua new file mode 100644 index 0000000000..c26ceeaedc --- /dev/null +++ b/test/functional/autocmd/textyankpost_spec.lua @@ -0,0 +1,216 @@ +local helpers = require('test.functional.helpers') +local clear, eval, eq = helpers.clear, helpers.eval, helpers.eq +local feed, execute, expect, command = helpers.feed, helpers.execute, helpers.expect, helpers.command +local curbufmeths, funcs, neq = helpers.curbufmeths, helpers.funcs, helpers.neq + +describe('TextYankPost', function() + before_each(function() + clear() + + -- emulate the clipboard so system clipboard isn't affected + execute('let &rtp = "test/functional/fixtures,".&rtp') + + execute('let g:count = 0') + execute('autocmd TextYankPost * let g:event = copy(v:event)') + execute('autocmd TextYankPost * let g:count += 1') + + curbufmeths.set_lines(0, -1, true, { + 'foo\0bar', + 'baz text', + }) + end) + + it('is executed after yank and handles register types', function() + feed('yy') + eq({ + operator = 'y', + regcontents = { 'foo\nbar' }, + regname = '', + regtype = 'V' + }, eval('g:event')) + eq(1, eval('g:count')) + + -- v:event is cleared after the autocommand is done + eq({}, eval('v:event')) + + feed('+yw') + eq({ + operator = 'y', + regcontents = { 'baz ' }, + regname = '', + regtype = 'v' + }, eval('g:event')) + eq(2, eval('g:count')) + + feed('<c-v>eky') + eq({ + operator = 'y', + regcontents = { 'foo', 'baz' }, + regname = '', + regtype = "\0223" -- ^V + block width + }, eval('g:event')) + eq(3, eval('g:count')) + end) + + it('makes v:event immutable', function() + feed('yy') + eq({ + operator = 'y', + regcontents = { 'foo\nbar' }, + regname = '', + regtype = 'V' + }, eval('g:event')) + + execute('set debug=msg') + -- the regcontents should not be changed without copy. + local status, err = pcall(command,'call extend(g:event.regcontents, ["more text"])') + eq(status,false) + neq(nil, string.find(err, ':E742:')) + + -- can't mutate keys inside the autocommand + execute('autocmd! TextYankPost * let v:event.regcontents = 0') + status, err = pcall(command,'normal yy') + eq(status,false) + neq(nil, string.find(err, ':E46:')) + + -- can't add keys inside the autocommand + execute('autocmd! TextYankPost * let v:event.mykey = 0') + status, err = pcall(command,'normal yy') + eq(status,false) + neq(nil, string.find(err, ':E742:')) + end) + + it('is not invoked recursively', function() + execute('autocmd TextYankPost * normal "+yy') + feed('yy') + eq({ + operator = 'y', + regcontents = { 'foo\nbar' }, + regname = '', + regtype = 'V' + }, eval('g:event')) + eq(1, eval('g:count')) + eq({ 'foo\nbar' }, funcs.getreg('+',1,1)) + end) + + it('is executed after delete and change', function() + feed('dw') + eq({ + operator = 'd', + regcontents = { 'foo' }, + regname = '', + regtype = 'v' + }, eval('g:event')) + eq(1, eval('g:count')) + + feed('dd') + eq({ + operator = 'd', + regcontents = { '\nbar' }, + regname = '', + regtype = 'V' + }, eval('g:event')) + eq(2, eval('g:count')) + + feed('cwspam<esc>') + eq({ + operator = 'c', + regcontents = { 'baz' }, + regname = '', + regtype = 'v' + }, eval('g:event')) + eq(3, eval('g:count')) + end) + + it('is not executed after black-hole operation', function() + feed('"_dd') + eq(0, eval('g:count')) + + feed('"_cwgood<esc>') + eq(0, eval('g:count')) + + expect([[ + good text]]) + feed('"_yy') + eq(0, eval('g:count')) + + execute('delete _') + eq(0, eval('g:count')) + end) + + it('gives the correct register name', function() + feed('$"byiw') + eq({ + operator = 'y', + regcontents = { 'bar' }, + regname = 'b', + regtype = 'v' + }, eval('g:event')) + + feed('"*yy') + eq({ + operator = 'y', + regcontents = { 'foo\nbar' }, + regname = '*', + regtype = 'V' + }, eval('g:event')) + + execute("set clipboard=unnamed") + + -- regname still shows the name the user requested + feed('yy') + eq({ + operator = 'y', + regcontents = { 'foo\nbar' }, + regname = '', + regtype = 'V' + }, eval('g:event')) + + feed('"*yy') + eq({ + operator = 'y', + regcontents = { 'foo\nbar' }, + regname = '*', + regtype = 'V' + }, eval('g:event')) + end) + + it('works with Ex commands', function() + execute('1delete +') + eq({ + operator = 'd', + regcontents = { 'foo\nbar' }, + regname = '+', + regtype = 'V' + }, eval('g:event')) + eq(1, eval('g:count')) + + execute('yank') + eq({ + operator = 'y', + regcontents = { 'baz text' }, + regname = '', + regtype = 'V' + }, eval('g:event')) + eq(2, eval('g:count')) + + execute('normal yw') + eq({ + operator = 'y', + regcontents = { 'baz ' }, + regname = '', + regtype = 'v' + }, eval('g:event')) + eq(3, eval('g:count')) + + execute('normal! dd') + eq({ + operator = 'd', + regcontents = { 'baz text' }, + regname = '', + regtype = 'V' + }, eval('g:event')) + eq(4, eval('g:count')) + end) + +end) diff --git a/test/functional/clipboard/clipboard_provider_spec.lua b/test/functional/clipboard/clipboard_provider_spec.lua index 98b8d2dd45..898ec556a2 100644 --- a/test/functional/clipboard/clipboard_provider_spec.lua +++ b/test/functional/clipboard/clipboard_provider_spec.lua @@ -4,7 +4,6 @@ local helpers = require('test.functional.helpers') local Screen = require('test.functional.ui.screen') local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert local execute, expect, eq, eval = helpers.execute, helpers.expect, helpers.eq, helpers.eval -local nvim, run, stop, restart = helpers.nvim, helpers.run, helpers.stop, helpers.restart local function basic_register_test(noblock) insert("some words") 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/eval/glob_spec.lua b/test/functional/eval/glob_spec.lua index f99a9e7d0e..c6bba46424 100644 --- a/test/functional/eval/glob_spec.lua +++ b/test/functional/eval/glob_spec.lua @@ -1,3 +1,4 @@ +local lfs = require('lfs') local helpers = require('test.functional.helpers') local clear, execute, eval, eq = helpers.clear, helpers.execute, helpers.eval, helpers.eq diff --git a/test/functional/eval/json_functions_spec.lua b/test/functional/eval/json_functions_spec.lua new file mode 100644 index 0000000000..b32688a9d2 --- /dev/null +++ b/test/functional/eval/json_functions_spec.lua @@ -0,0 +1,800 @@ +local helpers = require('test.functional.helpers') +local clear = helpers.clear +local funcs = helpers.funcs +local meths = helpers.meths +local eq = helpers.eq +local eval = helpers.eval +local execute = helpers.execute +local exc_exec = helpers.exc_exec +local redir_exec = helpers.redir_exec +local NIL = helpers.NIL + +describe('json_decode() function', function() + local restart = function(cmd) + clear(cmd) + execute('language C') + execute([[ + function Eq(exp, act) + let act = a:act + let exp = a:exp + if type(exp) != type(act) + return 0 + endif + if type(exp) == type({}) + if sort(keys(exp)) !=# sort(keys(act)) + return 0 + endif + if sort(keys(exp)) ==# ['_TYPE', '_VAL'] + let exp_typ = v:msgpack_types[exp._TYPE] + let act_typ = act._TYPE + if exp_typ isnot act_typ + return 0 + endif + return Eq(exp._VAL, act._VAL) + else + return empty(filter(copy(exp), '!Eq(v:val, act[v:key])')) + endif + else + if type(exp) == type([]) + if len(exp) != len(act) + return 0 + endif + return empty(filter(copy(exp), '!Eq(v:val, act[v:key])')) + endif + return exp ==# act + endif + return 1 + endfunction + ]]) + execute([[ + function EvalEq(exp, act_expr) + let act = eval(a:act_expr) + if Eq(a:exp, act) + return 1 + else + return string(act) + endif + endfunction + ]]) + end + before_each(restart) + + local speq = function(expected, actual_expr) + eq(1, funcs.EvalEq(expected, actual_expr)) + end + + it('accepts readfile()-style list', function() + eq({Test=1}, funcs.json_decode({ + '{', + '\t"Test": 1', + '}', + })) + end) + + it('accepts strings with newlines', function() + eq({Test=1}, funcs.json_decode([[ + { + "Test": 1 + } + ]])) + end) + + it('parses null, true, false', function() + eq(NIL, funcs.json_decode('null')) + eq(true, funcs.json_decode('true')) + eq(false, funcs.json_decode('false')) + end) + + it('fails to parse incomplete null, true, false', function() + eq('Vim(call):E474: Expected null: n', + exc_exec('call json_decode("n")')) + eq('Vim(call):E474: Expected null: nu', + exc_exec('call json_decode("nu")')) + eq('Vim(call):E474: Expected null: nul', + exc_exec('call json_decode("nul")')) + eq('Vim(call):E474: Expected null: nul\n\t', + exc_exec('call json_decode("nul\\n\\t")')) + + eq('Vim(call):E474: Expected true: t', + exc_exec('call json_decode("t")')) + eq('Vim(call):E474: Expected true: tr', + exc_exec('call json_decode("tr")')) + eq('Vim(call):E474: Expected true: tru', + exc_exec('call json_decode("tru")')) + eq('Vim(call):E474: Expected true: tru\t\n', + exc_exec('call json_decode("tru\\t\\n")')) + + eq('Vim(call):E474: Expected false: f', + exc_exec('call json_decode("f")')) + eq('Vim(call):E474: Expected false: fa', + exc_exec('call json_decode("fa")')) + eq('Vim(call):E474: Expected false: fal', + exc_exec('call json_decode("fal")')) + eq('Vim(call):E474: Expected false: fal <', + exc_exec('call json_decode(" fal <")')) + eq('Vim(call):E474: Expected false: fals', + exc_exec('call json_decode("fals")')) + end) + + it('parses integer numbers', function() + eq(100000, funcs.json_decode('100000')) + eq(-100000, funcs.json_decode('-100000')) + eq(100000, funcs.json_decode(' 100000 ')) + eq(-100000, funcs.json_decode(' -100000 ')) + eq(0, funcs.json_decode('0')) + eq(0, funcs.json_decode('-0')) + end) + + it('fails to parse +numbers and .number', function() + eq('Vim(call):E474: Unidentified byte: +1000', + exc_exec('call json_decode("+1000")')) + eq('Vim(call):E474: Unidentified byte: .1000', + exc_exec('call json_decode(".1000")')) + end) + + it('fails to parse numbers with leading zeroes', function() + eq('Vim(call):E474: Leading zeroes are not allowed: 00.1', + exc_exec('call json_decode("00.1")')) + eq('Vim(call):E474: Leading zeroes are not allowed: 01', + exc_exec('call json_decode("01")')) + eq('Vim(call):E474: Leading zeroes are not allowed: -01', + exc_exec('call json_decode("-01")')) + eq('Vim(call):E474: Leading zeroes are not allowed: -001.0', + exc_exec('call json_decode("-001.0")')) + end) + + it('fails to parse incomplete numbers', function() + eq('Vim(call):E474: Missing number after minus sign: -.1', + exc_exec('call json_decode("-.1")')) + eq('Vim(call):E474: Missing number after minus sign: -', + exc_exec('call json_decode("-")')) + eq('Vim(call):E474: Missing number after decimal dot: -1.', + exc_exec('call json_decode("-1.")')) + eq('Vim(call):E474: Missing number after decimal dot: 0.', + exc_exec('call json_decode("0.")')) + eq('Vim(call):E474: Missing exponent: 0.0e', + exc_exec('call json_decode("0.0e")')) + eq('Vim(call):E474: Missing exponent: 0.0e+', + exc_exec('call json_decode("0.0e+")')) + eq('Vim(call):E474: Missing exponent: 0.0e-', + exc_exec('call json_decode("0.0e-")')) + eq('Vim(call):E474: Missing exponent: 0.0e-', + exc_exec('call json_decode("0.0e-")')) + eq('Vim(call):E474: Missing number after decimal dot: 1.e5', + exc_exec('call json_decode("1.e5")')) + eq('Vim(call):E474: Missing number after decimal dot: 1.e+5', + exc_exec('call json_decode("1.e+5")')) + eq('Vim(call):E474: Missing number after decimal dot: 1.e+', + exc_exec('call json_decode("1.e+")')) + end) + + it('parses floating-point numbers', function() + eq('100000.0', eval('string(json_decode("100000.0"))')) + eq(100000.5, funcs.json_decode('100000.5')) + eq(-100000.5, funcs.json_decode('-100000.5')) + eq(-100000.5e50, funcs.json_decode('-100000.5e50')) + eq(100000.5e50, funcs.json_decode('100000.5e50')) + eq(100000.5e50, funcs.json_decode('100000.5e+50')) + eq(-100000.5e-50, funcs.json_decode('-100000.5e-50')) + eq(100000.5e-50, funcs.json_decode('100000.5e-50')) + eq(100000e-50, funcs.json_decode('100000e-50')) + eq(0.5, funcs.json_decode('0.5')) + eq(0.005, funcs.json_decode('0.005')) + eq(0.005, funcs.json_decode('0.00500')) + eq(0.5, funcs.json_decode('0.00500e+002')) + eq(0.00005, funcs.json_decode('0.00500e-002')) + + eq(-0.0, funcs.json_decode('-0.0')) + eq(-0.0, funcs.json_decode('-0.0e0')) + eq(-0.0, funcs.json_decode('-0.0e+0')) + eq(-0.0, funcs.json_decode('-0.0e-0')) + eq(-0.0, funcs.json_decode('-0e-0')) + eq(-0.0, funcs.json_decode('-0e-2')) + eq(-0.0, funcs.json_decode('-0e+2')) + + eq(0.0, funcs.json_decode('0.0')) + eq(0.0, funcs.json_decode('0.0e0')) + eq(0.0, funcs.json_decode('0.0e+0')) + eq(0.0, funcs.json_decode('0.0e-0')) + eq(0.0, funcs.json_decode('0e-0')) + eq(0.0, funcs.json_decode('0e-2')) + eq(0.0, funcs.json_decode('0e+2')) + end) + + it('fails to parse numbers with spaces inside', function() + eq('Vim(call):E474: Missing number after minus sign: - 1000', + exc_exec('call json_decode("- 1000")')) + eq('Vim(call):E474: Missing number after decimal dot: 0. ', + exc_exec('call json_decode("0. ")')) + eq('Vim(call):E474: Missing number after decimal dot: 0. 0', + exc_exec('call json_decode("0. 0")')) + eq('Vim(call):E474: Missing exponent: 0.0e 1', + exc_exec('call json_decode("0.0e 1")')) + eq('Vim(call):E474: Missing exponent: 0.0e+ 1', + exc_exec('call json_decode("0.0e+ 1")')) + eq('Vim(call):E474: Missing exponent: 0.0e- 1', + exc_exec('call json_decode("0.0e- 1")')) + end) + + it('fails to parse "," and ":"', function() + eq('Vim(call):E474: Comma not inside container: , ', + exc_exec('call json_decode(" , ")')) + eq('Vim(call):E474: Colon not inside container: : ', + exc_exec('call json_decode(" : ")')) + end) + + it('parses empty containers', function() + eq({}, funcs.json_decode('[]')) + eq('[]', eval('string(json_decode("[]"))')) + end) + + it('fails to parse "[" and "{"', function() + eq('Vim(call):E474: Unexpected end of input: {', + exc_exec('call json_decode("{")')) + eq('Vim(call):E474: Unexpected end of input: [', + exc_exec('call json_decode("[")')) + end) + + it('fails to parse "}" and "]"', function() + eq('Vim(call):E474: No container to close: ]', + exc_exec('call json_decode("]")')) + eq('Vim(call):E474: No container to close: }', + exc_exec('call json_decode("}")')) + end) + + it('fails to parse containers which are closed by different brackets', + function() + eq('Vim(call):E474: Closing dictionary with square bracket: ]', + exc_exec('call json_decode("{]")')) + eq('Vim(call):E474: Closing list with curly bracket: }', + exc_exec('call json_decode("[}")')) + end) + + it('fails to parse concat inside container', function() + eq('Vim(call):E474: Expected comma before list item: []]', + exc_exec('call json_decode("[[][]]")')) + eq('Vim(call):E474: Expected comma before list item: {}]', + exc_exec('call json_decode("[{}{}]")')) + eq('Vim(call):E474: Expected comma before list item: ]', + exc_exec('call json_decode("[1 2]")')) + eq('Vim(call):E474: Expected comma before dictionary key: ": 4}', + exc_exec('call json_decode("{\\"1\\": 2 \\"3\\": 4}")')) + eq('Vim(call):E474: Expected colon before dictionary value: , "3" 4}', + exc_exec('call json_decode("{\\"1\\" 2, \\"3\\" 4}")')) + end) + + it('fails to parse containers with leading comma or colon', function() + eq('Vim(call):E474: Leading comma: ,}', + exc_exec('call json_decode("{,}")')) + eq('Vim(call):E474: Leading comma: ,]', + exc_exec('call json_decode("[,]")')) + eq('Vim(call):E474: Using colon not in dictionary: :]', + exc_exec('call json_decode("[:]")')) + eq('Vim(call):E474: Unexpected colon: :}', + exc_exec('call json_decode("{:}")')) + end) + + it('fails to parse containers with trailing comma', function() + eq('Vim(call):E474: Trailing comma: ]', + exc_exec('call json_decode("[1,]")')) + eq('Vim(call):E474: Trailing comma: }', + exc_exec('call json_decode("{\\"1\\": 2,}")')) + end) + + it('fails to parse dictionaries with missing value', function() + eq('Vim(call):E474: Expected value after colon: }', + exc_exec('call json_decode("{\\"1\\":}")')) + eq('Vim(call):E474: Expected value: }', + exc_exec('call json_decode("{\\"1\\"}")')) + end) + + it('fails to parse containers with two commas or colons', function() + eq('Vim(call):E474: Duplicate comma: , "2": 2}', + exc_exec('call json_decode("{\\"1\\": 1,, \\"2\\": 2}")')) + eq('Vim(call):E474: Duplicate comma: , "2", 2]', + exc_exec('call json_decode("[\\"1\\", 1,, \\"2\\", 2]")')) + eq('Vim(call):E474: Duplicate colon: : 2}', + exc_exec('call json_decode("{\\"1\\": 1, \\"2\\":: 2}")')) + eq('Vim(call):E474: Comma after colon: , 2}', + exc_exec('call json_decode("{\\"1\\": 1, \\"2\\":, 2}")')) + eq('Vim(call):E474: Unexpected colon: : "2": 2}', + exc_exec('call json_decode("{\\"1\\": 1,: \\"2\\": 2}")')) + eq('Vim(call):E474: Unexpected colon: :, "2": 2}', + exc_exec('call json_decode("{\\"1\\": 1:, \\"2\\": 2}")')) + end) + + it('fails to parse concat of two values', function() + eq('Vim(call):E474: Trailing characters: []', + exc_exec('call json_decode("{}[]")')) + end) + + it('parses containers', function() + eq({1}, funcs.json_decode('[1]')) + eq({NIL, 1}, funcs.json_decode('[null, 1]')) + eq({['1']=2}, funcs.json_decode('{"1": 2}')) + eq({['1']=2, ['3']={{['4']={['5']={{}, 1}}}}}, + funcs.json_decode('{"1": 2, "3": [{"4": {"5": [[], 1]}}]}')) + end) + + it('fails to parse incomplete strings', function() + eq('Vim(call):E474: Expected string end: \t"', + exc_exec('call json_decode("\\t\\"")')) + eq('Vim(call):E474: Expected string end: \t"abc', + exc_exec('call json_decode("\\t\\"abc")')) + eq('Vim(call):E474: Unfinished escape sequence: \t"abc\\', + exc_exec('call json_decode("\\t\\"abc\\\\")')) + eq('Vim(call):E474: Unfinished unicode escape sequence: \t"abc\\u', + exc_exec('call json_decode("\\t\\"abc\\\\u")')) + eq('Vim(call):E474: Unfinished unicode escape sequence: \t"abc\\u0', + exc_exec('call json_decode("\\t\\"abc\\\\u0")')) + eq('Vim(call):E474: Unfinished unicode escape sequence: \t"abc\\u00', + exc_exec('call json_decode("\\t\\"abc\\\\u00")')) + eq('Vim(call):E474: Unfinished unicode escape sequence: \t"abc\\u000', + exc_exec('call json_decode("\\t\\"abc\\\\u000")')) + eq('Vim(call):E474: Expected four hex digits after \\u: \\u" ', + exc_exec('call json_decode("\\t\\"abc\\\\u\\" ")')) + eq('Vim(call):E474: Expected four hex digits after \\u: \\u0" ', + exc_exec('call json_decode("\\t\\"abc\\\\u0\\" ")')) + eq('Vim(call):E474: Expected four hex digits after \\u: \\u00" ', + exc_exec('call json_decode("\\t\\"abc\\\\u00\\" ")')) + eq('Vim(call):E474: Expected four hex digits after \\u: \\u000" ', + exc_exec('call json_decode("\\t\\"abc\\\\u000\\" ")')) + eq('Vim(call):E474: Expected string end: \t"abc\\u0000', + exc_exec('call json_decode("\\t\\"abc\\\\u0000")')) + end) + + it('fails to parse unknown escape sequnces', function() + eq('Vim(call):E474: Unknown escape sequence: \\a"', + exc_exec('call json_decode("\\t\\"\\\\a\\"")')) + end) + + it('parses strings properly', function() + eq('\n', funcs.json_decode('"\\n"')) + eq('', funcs.json_decode('""')) + eq('\\/"\t\b\n\r\f', funcs.json_decode([["\\\/\"\t\b\n\r\f"]])) + eq('/a', funcs.json_decode([["\/a"]])) + -- Unicode characters: 2-byte, 3-byte, 4-byte + eq({ + '«', + 'ફ', + '\240\144\128\128', + }, funcs.json_decode({ + '[', + '"«",', + '"ફ",', + '"\240\144\128\128"', + ']', + })) + end) + + it('fails on strings with invalid bytes', function() + eq('Vim(call):E474: Only UTF-8 strings allowed: \255"', + exc_exec('call json_decode("\\t\\"\\xFF\\"")')) + eq('Vim(call):E474: ASCII control characters cannot be present inside string: ', + exc_exec('call json_decode(["\\"\\n\\""])')) + -- 0xC2 starts 2-byte unicode character + eq('Vim(call):E474: Only UTF-8 strings allowed: \194"', + exc_exec('call json_decode("\\t\\"\\xC2\\"")')) + -- 0xE0 0xAA starts 3-byte unicode character + eq('Vim(call):E474: Only UTF-8 strings allowed: \224"', + exc_exec('call json_decode("\\t\\"\\xE0\\"")')) + eq('Vim(call):E474: Only UTF-8 strings allowed: \224\170"', + exc_exec('call json_decode("\\t\\"\\xE0\\xAA\\"")')) + -- 0xF0 0x90 0x80 starts 4-byte unicode character + eq('Vim(call):E474: Only UTF-8 strings allowed: \240"', + exc_exec('call json_decode("\\t\\"\\xF0\\"")')) + eq('Vim(call):E474: Only UTF-8 strings allowed: \240\144"', + exc_exec('call json_decode("\\t\\"\\xF0\\x90\\"")')) + eq('Vim(call):E474: Only UTF-8 strings allowed: \240\144\128"', + exc_exec('call json_decode("\\t\\"\\xF0\\x90\\x80\\"")')) + -- 0xF9 0x80 0x80 0x80 starts 5-byte unicode character + eq('Vim(call):E474: Only UTF-8 strings allowed: \249"', + exc_exec('call json_decode("\\t\\"\\xF9\\"")')) + eq('Vim(call):E474: Only UTF-8 strings allowed: \249\128"', + exc_exec('call json_decode("\\t\\"\\xF9\\x80\\"")')) + eq('Vim(call):E474: Only UTF-8 strings allowed: \249\128\128"', + exc_exec('call json_decode("\\t\\"\\xF9\\x80\\x80\\"")')) + eq('Vim(call):E474: Only UTF-8 strings allowed: \249\128\128\128"', + exc_exec('call json_decode("\\t\\"\\xF9\\x80\\x80\\x80\\"")')) + -- 0xFC 0x90 0x80 0x80 0x80 starts 6-byte unicode character + eq('Vim(call):E474: Only UTF-8 strings allowed: \252"', + exc_exec('call json_decode("\\t\\"\\xFC\\"")')) + eq('Vim(call):E474: Only UTF-8 strings allowed: \252\144"', + exc_exec('call json_decode("\\t\\"\\xFC\\x90\\"")')) + eq('Vim(call):E474: Only UTF-8 strings allowed: \252\144\128"', + exc_exec('call json_decode("\\t\\"\\xFC\\x90\\x80\\"")')) + eq('Vim(call):E474: Only UTF-8 strings allowed: \252\144\128\128"', + exc_exec('call json_decode("\\t\\"\\xFC\\x90\\x80\\x80\\"")')) + eq('Vim(call):E474: Only UTF-8 strings allowed: \252\144\128\128\128"', + exc_exec('call json_decode("\\t\\"\\xFC\\x90\\x80\\x80\\x80\\"")')) + -- Specification does not allow unquoted characters above 0x10FFFF + eq('Vim(call):E474: Only UTF-8 code points up to U+10FFFF are allowed to appear unescaped: \249\128\128\128\128"', + exc_exec('call json_decode("\\t\\"\\xF9\\x80\\x80\\x80\\x80\\"")')) + eq('Vim(call):E474: Only UTF-8 code points up to U+10FFFF are allowed to appear unescaped: \252\144\128\128\128\128"', + exc_exec('call json_decode("\\t\\"\\xFC\\x90\\x80\\x80\\x80\\x80\\"")')) + -- '"\249\128\128\128\128"', + -- '"\252\144\128\128\128\128"', + end) + + it('parses surrogate pairs properly', function() + eq('\240\144\128\128', funcs.json_decode('"\\uD800\\uDC00"')) + eq('\237\160\128a\237\176\128', funcs.json_decode('"\\uD800a\\uDC00"')) + eq('\237\160\128\t\237\176\128', funcs.json_decode('"\\uD800\\t\\uDC00"')) + + eq('\237\160\128', funcs.json_decode('"\\uD800"')) + eq('\237\160\128a', funcs.json_decode('"\\uD800a"')) + eq('\237\160\128\t', funcs.json_decode('"\\uD800\\t"')) + + eq('\237\176\128', funcs.json_decode('"\\uDC00"')) + eq('\237\176\128a', funcs.json_decode('"\\uDC00a"')) + eq('\237\176\128\t', funcs.json_decode('"\\uDC00\\t"')) + + eq('\237\176\128', funcs.json_decode('"\\uDC00"')) + eq('a\237\176\128', funcs.json_decode('"a\\uDC00"')) + eq('\t\237\176\128', funcs.json_decode('"\\t\\uDC00"')) + + eq('\237\160\128¬', funcs.json_decode('"\\uD800\\u00AC"')) + + eq('\237\160\128\237\160\128', funcs.json_decode('"\\uD800\\uD800"')) + end) + + local sp_decode_eq = function(expected, json) + meths.set_var('__json', json) + speq(expected, 'json_decode(g:__json)') + execute('unlet! g:__json') + end + + it('parses strings with NUL properly', function() + sp_decode_eq({_TYPE='string', _VAL={'\n'}}, '"\\u0000"') + sp_decode_eq({_TYPE='string', _VAL={'\n', '\n'}}, '"\\u0000\\n\\u0000"') + sp_decode_eq({_TYPE='string', _VAL={'\n«\n'}}, '"\\u0000\\u00AB\\u0000"') + end) + + it('parses dictionaries with duplicate keys to special maps', function() + sp_decode_eq({_TYPE='map', _VAL={{'a', 1}, {'a', 2}}}, + '{"a": 1, "a": 2}') + sp_decode_eq({_TYPE='map', _VAL={{'b', 3}, {'a', 1}, {'a', 2}}}, + '{"b": 3, "a": 1, "a": 2}') + sp_decode_eq({_TYPE='map', _VAL={{'b', 3}, {'a', 1}, {'c', 4}, {'a', 2}}}, + '{"b": 3, "a": 1, "c": 4, "a": 2}') + sp_decode_eq({_TYPE='map', _VAL={{'b', 3}, {'a', 1}, {'c', 4}, {'a', 2}, {'c', 4}}}, + '{"b": 3, "a": 1, "c": 4, "a": 2, "c": 4}') + sp_decode_eq({{_TYPE='map', _VAL={{'b', 3}, {'a', 1}, {'c', 4}, {'a', 2}, {'c', 4}}}}, + '[{"b": 3, "a": 1, "c": 4, "a": 2, "c": 4}]') + sp_decode_eq({{d={_TYPE='map', _VAL={{'b', 3}, {'a', 1}, {'c', 4}, {'a', 2}, {'c', 4}}}}}, + '[{"d": {"b": 3, "a": 1, "c": 4, "a": 2, "c": 4}}]') + sp_decode_eq({1, {d={_TYPE='map', _VAL={{'b', 3}, {'a', 1}, {'c', 4}, {'a', 2}, {'c', 4}}}}}, + '[1, {"d": {"b": 3, "a": 1, "c": 4, "a": 2, "c": 4}}]') + sp_decode_eq({1, {a={}, d={_TYPE='map', _VAL={{'b', 3}, {'a', 1}, {'c', 4}, {'a', 2}, {'c', 4}}}}}, + '[1, {"a": [], "d": {"b": 3, "a": 1, "c": 4, "a": 2, "c": 4}}]') + end) + + it('parses dictionaries with empty keys to special maps', function() + sp_decode_eq({_TYPE='map', _VAL={{'', 4}}}, + '{"": 4}') + sp_decode_eq({_TYPE='map', _VAL={{'b', 3}, {'a', 1}, {'c', 4}, {'d', 2}, {'', 4}}}, + '{"b": 3, "a": 1, "c": 4, "d": 2, "": 4}') + sp_decode_eq({_TYPE='map', _VAL={{'', 3}, {'a', 1}, {'c', 4}, {'d', 2}, {'', 4}}}, + '{"": 3, "a": 1, "c": 4, "d": 2, "": 4}') + sp_decode_eq({{_TYPE='map', _VAL={{'', 3}, {'a', 1}, {'c', 4}, {'d', 2}, {'', 4}}}}, + '[{"": 3, "a": 1, "c": 4, "d": 2, "": 4}]') + end) + + it('parses dictionaries with keys with NUL bytes to special maps', function() + sp_decode_eq({_TYPE='map', _VAL={{{_TYPE='string', _VAL={'a\n', 'b'}}, 4}}}, + '{"a\\u0000\\nb": 4}') + sp_decode_eq({_TYPE='map', _VAL={{{_TYPE='string', _VAL={'a\n', 'b', ''}}, 4}}}, + '{"a\\u0000\\nb\\n": 4}') + sp_decode_eq({_TYPE='map', _VAL={{'b', 3}, {'a', 1}, {'c', 4}, {'d', 2}, {{_TYPE='string', _VAL={'\n'}}, 4}}}, + '{"b": 3, "a": 1, "c": 4, "d": 2, "\\u0000": 4}') + end) + + it('converts strings to latin1 when &encoding is latin1', function() + restart('set encoding=latin1') + eq('\171', funcs.json_decode('"\\u00AB"')) + sp_decode_eq({_TYPE='string', _VAL={'\n\171\n'}}, '"\\u0000\\u00AB\\u0000"') + end) + + it('fails to convert string to latin1 if it is impossible', function() + restart('set encoding=latin1') + eq('Vim(call):E474: Failed to convert string "ꯍ" from UTF-8', + exc_exec('call json_decode(\'"\\uABCD"\')')) + end) + + it('parses U+00C3 correctly', function() + eq('\195\131', funcs.json_decode('"\195\131"')) + end) + + it('fails to parse empty string', function() + eq('Vim(call):E474: Attempt to decode a blank string', + exc_exec('call json_decode("")')) + eq('Vim(call):E474: Attempt to decode a blank string', + exc_exec('call json_decode([])')) + eq('Vim(call):E474: Attempt to decode a blank string', + exc_exec('call json_decode([""])')) + eq('Vim(call):E474: Attempt to decode a blank string', + exc_exec('call json_decode(" ")')) + eq('Vim(call):E474: Attempt to decode a blank string', + exc_exec('call json_decode("\\t")')) + eq('Vim(call):E474: Attempt to decode a blank string', + exc_exec('call json_decode("\\n")')) + eq('Vim(call):E474: Attempt to decode a blank string', + exc_exec('call json_decode(" \\t\\n \\n\\t\\t \\n\\t\\n \\n \\t\\n\\t ")')) + end) + + it('accepts all spaces in every position where space may be put', function() + local s = ' \t\n\r \t\r\n \n\t\r \n\r\t \r\t\n \r\n\t\t \n\r\t \r\n\t\n \r\t\n\r \t\r \n\t\r\n \n \t\r\n \r\t\n\t \r\n\t\r \n\r \t\n\r\t \r \t\n\r \n\t\r\t \n\r\t\n \r\n \t\r\n\t' + local str = ('%s{%s"key"%s:%s[%s"val"%s,%s"val2"%s]%s,%s"key2"%s:%s1%s}%s'):gsub('%%s', s) + eq({key={'val', 'val2'}, key2=1}, funcs.json_decode(str)) + end) + + it('always treats input as UTF-8', function() + -- When &encoding is latin1 string "«" is U+00C2 U+00AB U+00C2: «Â. So if + -- '"«"' was parsed as latin1 json_decode would return three characters, and + -- only one U+00AB when this string is parsed as latin1. + restart('set encoding=latin1') + eq(('%c'):format(0xAB), funcs.json_decode('"«"')) + end) + + it('does not overflow when writing error message about decoding ["", ""]', + function() + eq('\nE474: Attempt to decode a blank string' + .. '\nE474: Failed to parse \n', + redir_exec('call json_decode(["", ""])')) + end) +end) + +describe('json_encode() function', function() + before_each(function() + clear() + execute('language C') + end) + + it('dumps strings', function() + eq('"Test"', funcs.json_encode('Test')) + eq('""', funcs.json_encode('')) + eq('"\\t"', funcs.json_encode('\t')) + eq('"\\n"', funcs.json_encode('\n')) + eq('"\\u001B"', funcs.json_encode('\27')) + eq('"þÿþ"', funcs.json_encode('þÿþ')) + end) + + it('dumps numbers', function() + eq('0', funcs.json_encode(0)) + eq('10', funcs.json_encode(10)) + eq('-10', funcs.json_encode(-10)) + end) + + it('dumps floats', function() + eq('0.0', eval('json_encode(0.0)')) + eq('10.5', funcs.json_encode(10.5)) + eq('-10.5', funcs.json_encode(-10.5)) + eq('-1.0e-5', funcs.json_encode(-1e-5)) + eq('1.0e50', eval('json_encode(1.0e50)')) + end) + + it('fails to dump NaN and infinite values', function() + eq('Vim(call):E474: Unable to represent NaN value in JSON', + exc_exec('call json_encode(str2float("nan"))')) + eq('Vim(call):E474: Unable to represent infinity in JSON', + exc_exec('call json_encode(str2float("inf"))')) + eq('Vim(call):E474: Unable to represent infinity in JSON', + exc_exec('call json_encode(-str2float("inf"))')) + end) + + it('dumps lists', function() + eq('[]', funcs.json_encode({})) + eq('[[]]', funcs.json_encode({{}})) + eq('[[], []]', funcs.json_encode({{}, {}})) + end) + + it('dumps dictionaries', function() + eq('{}', eval('json_encode({})')) + eq('{"d": []}', funcs.json_encode({d={}})) + eq('{"d": [], "e": []}', funcs.json_encode({d={}, e={}})) + end) + + it('cannot dump generic mapping with generic mapping keys and values', + function() + execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": []}') + execute('let todumpv1 = {"_TYPE": v:msgpack_types.map, "_VAL": []}') + execute('let todumpv2 = {"_TYPE": v:msgpack_types.map, "_VAL": []}') + execute('call add(todump._VAL, [todumpv1, todumpv2])') + eq('Vim(call):E474: Invalid key in special dictionary', exc_exec('call json_encode(todump)')) + end) + + it('cannot dump generic mapping with ext key', function() + execute('let todump = {"_TYPE": v:msgpack_types.ext, "_VAL": [5, ["",""]]}') + execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}') + eq('Vim(call):E474: Invalid key in special dictionary', exc_exec('call json_encode(todump)')) + end) + + it('cannot dump generic mapping with array key', function() + execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": [5, [""]]}') + execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}') + eq('Vim(call):E474: Invalid key in special dictionary', exc_exec('call json_encode(todump)')) + end) + + it('cannot dump generic mapping with UINT64_MAX key', function() + execute('let todump = {"_TYPE": v:msgpack_types.integer}') + execute('let todump._VAL = [1, 3, 0x7FFFFFFF, 0x7FFFFFFF]') + execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}') + eq('Vim(call):E474: Invalid key in special dictionary', exc_exec('call json_encode(todump)')) + end) + + it('cannot dump generic mapping with floating-point key', function() + execute('let todump = {"_TYPE": v:msgpack_types.float, "_VAL": 0.125}') + execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}') + eq('Vim(call):E474: Invalid key in special dictionary', exc_exec('call json_encode(todump)')) + end) + + it('can dump generic mapping with STR special key and NUL', function() + execute('let todump = {"_TYPE": v:msgpack_types.string, "_VAL": ["\\n"]}') + execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}') + eq('{"\\u0000": 1}', eval('json_encode(todump)')) + end) + + it('can dump generic mapping with BIN special key and NUL', function() + execute('let todump = {"_TYPE": v:msgpack_types.binary, "_VAL": ["\\n"]}') + execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}') + eq('{"\\u0000": 1}', eval('json_encode(todump)')) + end) + + it('can dump STR special mapping with NUL and NL', function() + execute('let todump = {"_TYPE": v:msgpack_types.string, "_VAL": ["\\n", ""]}') + eq('"\\u0000\\n"', eval('json_encode(todump)')) + end) + + it('can dump BIN special mapping with NUL and NL', function() + execute('let todump = {"_TYPE": v:msgpack_types.binary, "_VAL": ["\\n", ""]}') + eq('"\\u0000\\n"', eval('json_encode(todump)')) + end) + + it('cannot dump special ext mapping', function() + execute('let todump = {"_TYPE": v:msgpack_types.ext, "_VAL": [5, ["",""]]}') + eq('Vim(call):E474: Unable to convert EXT string to JSON', exc_exec('call json_encode(todump)')) + end) + + it('can dump special array mapping', function() + execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": [5, [""]]}') + eq('[5, [""]]', eval('json_encode(todump)')) + end) + + it('can dump special UINT64_MAX mapping', function() + execute('let todump = {"_TYPE": v:msgpack_types.integer}') + execute('let todump._VAL = [1, 3, 0x7FFFFFFF, 0x7FFFFFFF]') + eq('18446744073709551615', eval('json_encode(todump)')) + end) + + it('can dump special INT64_MIN mapping', function() + execute('let todump = {"_TYPE": v:msgpack_types.integer}') + execute('let todump._VAL = [-1, 2, 0, 0]') + eq('-9223372036854775808', eval('json_encode(todump)')) + end) + + it('can dump special BOOLEAN true mapping', function() + execute('let todump = {"_TYPE": v:msgpack_types.boolean, "_VAL": 1}') + eq('true', eval('json_encode(todump)')) + end) + + it('can dump special BOOLEAN false mapping', function() + execute('let todump = {"_TYPE": v:msgpack_types.boolean, "_VAL": 0}') + eq('false', eval('json_encode(todump)')) + end) + + it('can dump special NIL mapping', function() + execute('let todump = {"_TYPE": v:msgpack_types.nil, "_VAL": 0}') + eq('null', eval('json_encode(todump)')) + end) + + it('fails to dump a function reference', function() + eq('Vim(call):E474: Error while dumping encode_tv2json() argument, itself: attempt to dump function reference', + exc_exec('call json_encode(function("tr"))')) + end) + + it('fails to dump a function reference in a list', function() + eq('Vim(call):E474: Error while dumping encode_tv2json() argument, index 0: attempt to dump function reference', + exc_exec('call json_encode([function("tr")])')) + end) + + it('fails to dump a recursive list', function() + execute('let todump = [[[]]]') + execute('call add(todump[0][0], todump)') + eq('Vim(call):E724: unable to correctly dump variable with self-referencing container', + exc_exec('call json_encode(todump)')) + end) + + it('fails to dump a recursive dict', function() + execute('let todump = {"d": {"d": {}}}') + execute('call extend(todump.d.d, {"d": todump})') + eq('Vim(call):E724: unable to correctly dump variable with self-referencing container', + exc_exec('call json_encode([todump])')) + end) + + it('can dump dict with two same dicts inside', function() + execute('let inter = {}') + execute('let todump = {"a": inter, "b": inter}') + eq('{"a": {}, "b": {}}', eval('json_encode(todump)')) + end) + + it('can dump list with two same lists inside', function() + execute('let inter = []') + execute('let todump = [inter, inter]') + eq('[[], []]', eval('json_encode(todump)')) + end) + + it('fails to dump a recursive list in a special dict', function() + execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": []}') + execute('call add(todump._VAL, todump)') + eq('Vim(call):E724: unable to correctly dump variable with self-referencing container', + exc_exec('call json_encode(todump)')) + end) + + it('fails to dump a recursive (val) map in a special dict', function() + execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": []}') + execute('call add(todump._VAL, ["", todump])') + eq('Vim(call):E724: unable to correctly dump variable with self-referencing container', + exc_exec('call json_encode([todump])')) + end) + + it('fails to dump a recursive (val) map in a special dict, _VAL reference', function() + execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [["", []]]}') + execute('call add(todump._VAL[0][1], todump._VAL)') + eq('Vim(call):E724: unable to correctly dump variable with self-referencing container', + exc_exec('call json_encode(todump)')) + end) + + it('fails to dump a recursive (val) special list in a special dict', + function() + execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": []}') + execute('call add(todump._VAL, ["", todump._VAL])') + eq('Vim(call):E724: unable to correctly dump variable with self-referencing container', + exc_exec('call json_encode(todump)')) + end) + + it('fails when called with no arguments', function() + eq('Vim(call):E119: Not enough arguments for function: json_encode', + exc_exec('call json_encode()')) + end) + + it('fails when called with two arguments', function() + eq('Vim(call):E118: Too many arguments for function: json_encode', + exc_exec('call json_encode(["", ""], 1)')) + end) + + it('converts strings from latin1 when &encoding is latin1', function() + clear('set encoding=latin1') + eq('"\\u00AB"', funcs.json_encode('\171')) + eq('"\\u0000\\u00AB\\u0000"', eval('json_encode({"_TYPE": v:msgpack_types.string, "_VAL": ["\\n\171\\n"]})')) + end) + + it('ignores improper values in &isprint', function() + meths.set_option('isprint', '1') + eq(1, eval('"\1" =~# "\\\\p"')) + eq('"\\u0001"', funcs.json_encode('\1')) + end) + + it('fails when using surrogate character in a UTF-8 string', function() + eq('Vim(call):E474: UTF-8 string contains code point which belongs to a surrogate pair: \237\160\128', + exc_exec('call json_encode("\237\160\128")')) + eq('Vim(call):E474: UTF-8 string contains code point which belongs to a surrogate pair: \237\175\191', + exc_exec('call json_encode("\237\175\191")')) + end) + + it('dumps control characters as expected', function() + eq([["\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000B\f\r\u000E\u000F\u0010\u0011\u0012\u0013"]], + eval('json_encode({"_TYPE": v:msgpack_types.string, "_VAL": ["\n\1\2\3\4\5\6\7\8\9", "\11\12\13\14\15\16\17\18\19"]})')) + end) + + it('can dump NULL string', function() + eq('""', eval('json_encode($XXX_UNEXISTENT_VAR_XXX)')) + end) + + it('can dump NULL list', function() + eq('[]', eval('json_encode(v:_null_list)')) + end) + + it('can dump NULL dictionary', function() + eq('{}', eval('json_encode(v:_null_dict)')) + end) +end) diff --git a/test/functional/eval/msgpack_functions_spec.lua b/test/functional/eval/msgpack_functions_spec.lua index 9a7b630f64..9e501353a5 100644 --- a/test/functional/eval/msgpack_functions_spec.lua +++ b/test/functional/eval/msgpack_functions_spec.lua @@ -1,7 +1,8 @@ local helpers = require('test.functional.helpers') -local clear, feed, execute = helpers.clear, helpers.feed, helpers.execute -local eval, eq, neq = helpers.eval, helpers.eq, helpers.neq -local execute, source = helpers.execute, helpers.source +local clear = helpers.clear +local funcs = helpers.funcs +local eval, eq = helpers.eval, helpers.eq +local execute = helpers.execute local nvim = helpers.nvim local exc_exec = helpers.exc_exec @@ -382,30 +383,32 @@ describe('msgpack*() functions', function() eq({"\n"}, eval('parsed')) eq(1, eval('dumped ==# dumped2')) end) + + it('dump and restore special mapping with floating-point value', function() + execute('let todump = {"_TYPE": v:msgpack_types.float, "_VAL": 0.125}') + eq({0.125}, eval('msgpackparse(msgpackdump([todump]))')) + end) end) describe('msgpackparse() function', function() before_each(clear) - it('restores nil as special dict', function() + it('restores nil as v:null', function() execute('let dumped = ["\\xC0"]') execute('let parsed = msgpackparse(dumped)') - eq({{_TYPE={}, _VAL=0}}, eval('parsed')) - eq(1, eval('g:parsed[0]._TYPE is v:msgpack_types.nil')) + eq('[v:null]', eval('string(parsed)')) end) - it('restores boolean false as zero', function() + it('restores boolean false as v:false', function() execute('let dumped = ["\\xC2"]') execute('let parsed = msgpackparse(dumped)') - eq({{_TYPE={}, _VAL=0}}, eval('parsed')) - eq(1, eval('g:parsed[0]._TYPE is v:msgpack_types.boolean')) + eq({false}, eval('parsed')) end) - it('restores boolean true as one', function() + it('restores boolean true as v:true', function() execute('let dumped = ["\\xC3"]') execute('let parsed = msgpackparse(dumped)') - eq({{_TYPE={}, _VAL=1}}, eval('parsed')) - eq(1, eval('g:parsed[0]._TYPE is v:msgpack_types.boolean')) + eq({true}, eval('parsed')) end) it('restores FIXSTR as special dict', function() @@ -454,9 +457,9 @@ describe('msgpackparse() function', function() it('msgpackparse(systemlist(...)) does not segfault. #3135', function() local cmd = "sort(keys(msgpackparse(systemlist('" ..helpers.nvim_prog.." --api-info'))[0]))" - local api_info = eval(cmd) - api_info = eval(cmd) -- do it again (try to force segfault) - api_info = eval(cmd) -- do it again + eval(cmd) + eval(cmd) -- do it again (try to force segfault) + local api_info = eval(cmd) -- do it again eq({'error_types', 'functions', 'types'}, api_info) end) @@ -512,56 +515,78 @@ describe('msgpackdump() function', function() eq({'\129\128\128'}, eval('msgpackdump([todump])')) end) - it('can dump generic mapping with ext', function() + it('can dump v:true', function() + eq({'\195'}, funcs.msgpackdump({true})) + end) + + it('can dump v:false', function() + eq({'\194'}, funcs.msgpackdump({false})) + end) + + it('can v:null', function() + execute('let todump = v:null') + end) + + it('can dump special bool mapping (true)', function() + execute('let todump = {"_TYPE": v:msgpack_types.boolean, "_VAL": 1}') + eq({'\195'}, eval('msgpackdump([todump])')) + end) + + it('can dump special bool mapping (false)', function() + execute('let todump = {"_TYPE": v:msgpack_types.boolean, "_VAL": 0}') + eq({'\194'}, eval('msgpackdump([todump])')) + end) + + it('can dump special nil mapping', function() + execute('let todump = {"_TYPE": v:msgpack_types.nil, "_VAL": 0}') + eq({'\192'}, eval('msgpackdump([todump])')) + end) + + it('can dump special ext mapping', function() execute('let todump = {"_TYPE": v:msgpack_types.ext, "_VAL": [5, ["",""]]}') eq({'\212\005', ''}, eval('msgpackdump([todump])')) end) - it('can dump generic mapping with array', function() + it('can dump special array mapping', function() execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": [5, [""]]}') eq({'\146\005\145\196\n'}, eval('msgpackdump([todump])')) end) - it('can dump generic mapping with UINT64_MAX', function() + it('can dump special UINT64_MAX mapping', function() execute('let todump = {"_TYPE": v:msgpack_types.integer}') execute('let todump._VAL = [1, 3, 0x7FFFFFFF, 0x7FFFFFFF]') eq({'\207\255\255\255\255\255\255\255\255'}, eval('msgpackdump([todump])')) end) - it('can dump generic mapping with INT64_MIN', function() + it('can dump special INT64_MIN mapping', function() execute('let todump = {"_TYPE": v:msgpack_types.integer}') execute('let todump._VAL = [-1, 2, 0, 0]') eq({'\211\128\n\n\n\n\n\n\n'}, eval('msgpackdump([todump])')) end) - it('dump and restore generic mapping with floating-point value', function() - execute('let todump = {"_TYPE": v:msgpack_types.float, "_VAL": 0.125}') - eq({0.125}, eval('msgpackparse(msgpackdump([todump]))')) - end) - it('fails to dump a function reference', function() execute('let Todump = function("tr")') - eq('Vim(call):E475: Invalid argument: attempt to dump function reference', + eq('Vim(call):E951: Error while dumping msgpackdump() argument, index 0, itself: attempt to dump function reference', exc_exec('call msgpackdump([Todump])')) end) it('fails to dump a function reference in a list', function() execute('let todump = [function("tr")]') - eq('Vim(call):E475: Invalid argument: attempt to dump function reference', + eq('Vim(call):E951: Error while dumping msgpackdump() argument, index 0, index 0: attempt to dump function reference', exc_exec('call msgpackdump([todump])')) end) it('fails to dump a recursive list', function() execute('let todump = [[[]]]') execute('call add(todump[0][0], todump)') - eq('Vim(call):E475: Invalid argument: container references itself', + eq('Vim(call):E952: Unable to dump msgpackdump() argument, index 0: container references itself in index 0, index 0, index 0', exc_exec('call msgpackdump([todump])')) end) it('fails to dump a recursive dict', function() execute('let todump = {"d": {"d": {}}}') execute('call extend(todump.d.d, {"d": todump})') - eq('Vim(call):E475: Invalid argument: container references itself', + eq('Vim(call):E952: Unable to dump msgpackdump() argument, index 0: container references itself in key \'d\', key \'d\', key \'d\'', exc_exec('call msgpackdump([todump])')) end) @@ -580,21 +605,35 @@ describe('msgpackdump() function', function() it('fails to dump a recursive list in a special dict', function() execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": []}') execute('call add(todump._VAL, todump)') - eq('Vim(call):E475: Invalid argument: container references itself', + eq('Vim(call):E952: Unable to dump msgpackdump() argument, index 0: container references itself in index 0', exc_exec('call msgpackdump([todump])')) end) it('fails to dump a recursive (key) map in a special dict', function() - execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": []}') + execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": []}') execute('call add(todump._VAL, [todump, 0])') - eq('Vim(call):E475: Invalid argument: container references itself', + eq('Vim(call):E952: Unable to dump msgpackdump() argument, index 0: container references itself in index 1', exc_exec('call msgpackdump([todump])')) end) it('fails to dump a recursive (val) map in a special dict', function() - execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": []}') + execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": []}') execute('call add(todump._VAL, [0, todump])') - eq('Vim(call):E475: Invalid argument: container references itself', + eq('Vim(call):E952: Unable to dump msgpackdump() argument, index 0: container references itself in key 0 at index 0 from special map', + exc_exec('call msgpackdump([todump])')) + end) + + it('fails to dump a recursive (key) map in a special dict, _VAL reference', function() + execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[[], []]]}') + execute('call add(todump._VAL[0][0], todump._VAL)') + eq('Vim(call):E952: Unable to dump msgpackdump() argument, index 0: container references itself in key [[[[...@0], []]]] at index 0 from special map, index 0', + exc_exec('call msgpackdump([todump])')) + end) + + it('fails to dump a recursive (val) map in a special dict, _VAL reference', function() + execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[[], []]]}') + execute('call add(todump._VAL[0][1], todump._VAL)') + eq('Vim(call):E952: Unable to dump msgpackdump() argument, index 0: container references itself in key [] at index 0 from special map, index 0', exc_exec('call msgpackdump([todump])')) end) @@ -602,7 +641,7 @@ describe('msgpackdump() function', function() function() execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": []}') execute('call add(todump._VAL, [0, todump._VAL])') - eq('Vim(call):E475: Invalid argument: container references itself', + eq('Vim(call):E952: Unable to dump msgpackdump() argument, index 0: container references itself in index 0, index 1', exc_exec('call msgpackdump([todump])')) end) @@ -640,4 +679,25 @@ describe('msgpackdump() function', function() eq('Vim(call):E686: Argument of msgpackdump() must be a List', exc_exec('call msgpackdump(0.0)')) end) + + it('fails to dump special value', function() + for _, val in ipairs({'v:true', 'v:false', 'v:null'}) do + eq('Vim(call):E686: Argument of msgpackdump() must be a List', + exc_exec('call msgpackdump(' .. val .. ')')) + end + end) + + it('can dump NULL string', function() + eq({'\196\n'}, eval('msgpackdump([$XXX_UNEXISTENT_VAR_XXX])')) + eq({'\196\n'}, eval('msgpackdump([{"_TYPE": v:msgpack_types.binary, "_VAL": [$XXX_UNEXISTENT_VAR_XXX]}])')) + eq({'\160'}, eval('msgpackdump([{"_TYPE": v:msgpack_types.string, "_VAL": [$XXX_UNEXISTENT_VAR_XXX]}])')) + end) + + it('can dump NULL list', function() + eq({'\144'}, eval('msgpackdump([v:_null_list])')) + end) + + it('can dump NULL dictionary', function() + eq({'\128'}, eval('msgpackdump([v:_null_dict])')) + end) end) diff --git a/test/functional/eval/operators_spec.lua b/test/functional/eval/operators_spec.lua new file mode 100644 index 0000000000..bc9a17935c --- /dev/null +++ b/test/functional/eval/operators_spec.lua @@ -0,0 +1,28 @@ +local helpers = require('test.functional.helpers') +local eq = helpers.eq +local eval = helpers.eval +local clear = helpers.clear + +describe('Division operator', function() + before_each(clear) + + it('returns infinity on {positive}/0.0', function() + eq('str2float(\'inf\')', eval('string(1.0/0.0)')) + eq('str2float(\'inf\')', eval('string(1.0e-100/0.0)')) + eq('str2float(\'inf\')', eval('string(1.0e+100/0.0)')) + eq('str2float(\'inf\')', eval('string((1.0/0.0)/0.0)')) + end) + + it('returns -infinity on {negative}/0.0', function() + eq('-str2float(\'inf\')', eval('string((-1.0)/0.0)')) + eq('-str2float(\'inf\')', eval('string((-1.0e-100)/0.0)')) + eq('-str2float(\'inf\')', eval('string((-1.0e+100)/0.0)')) + eq('-str2float(\'inf\')', eval('string((-1.0/0.0)/0.0)')) + end) + + it('returns NaN on 0.0/0.0', function() + eq('str2float(\'nan\')', eval('string(0.0/0.0)')) + eq('str2float(\'nan\')', eval('string(-(0.0/0.0))')) + eq('str2float(\'nan\')', eval('string((-0.0)/0.0)')) + end) +end) diff --git a/test/functional/eval/printf_spec.lua b/test/functional/eval/printf_spec.lua new file mode 100644 index 0000000000..6180f4156a --- /dev/null +++ b/test/functional/eval/printf_spec.lua @@ -0,0 +1,60 @@ +local helpers = require('test.functional.helpers') +local clear = helpers.clear +local eq = helpers.eq +local funcs = helpers.funcs +local exc_exec = helpers.exc_exec + +describe('printf()', function() + before_each(clear) + + it('works with zero and %b', function() + eq('0', funcs.printf('%lb', 0)) + eq('0', funcs.printf('%llb', 0)) + eq('0', funcs.printf('%zb', 0)) + end) + it('works with one and %b', function() + eq('1', funcs.printf('%b', 1)) + eq('1', funcs.printf('%lb', 1)) + eq('1', funcs.printf('%llb', 1)) + eq('1', funcs.printf('%zb', 1)) + end) + it('works with 0xff and %b', function() + eq('11111111', funcs.printf('%b', 0xff)) + eq('11111111', funcs.printf('%lb', 0xff)) + eq('11111111', funcs.printf('%llb', 0xff)) + eq('11111111', funcs.printf('%zb', 0xff)) + end) + it('accepts width modifier with %b', function() + eq(' 1', funcs.printf('%3b', 1)) + end) + it('accepts prefix modifier with %b', function() + eq('0b1', funcs.printf('%#b', 1)) + end) + it('writes capital B with %B', function() + eq('0B1', funcs.printf('%#B', 1)) + end) + it('accepts prefix, zero-fill and width modifiers with %b', function() + eq('0b001', funcs.printf('%#05b', 1)) + end) + it('accepts prefix and width modifiers with %b', function() + eq(' 0b1', funcs.printf('%#5b', 1)) + end) + it('does not write prefix for zero with prefix and width modifier used with %b', function() + eq(' 0', funcs.printf('%#5b', 0)) + end) + it('accepts precision modifier with %b', function() + eq('00000', funcs.printf('%.5b', 0)) + end) + it('accepts all modifiers with %b at once', function() + -- zero-fill modifier is ignored when used with left-align + -- force-sign and add-blank are ignored + -- use-grouping-characters modifier is ignored always + eq('0b00011 ', funcs.printf('% \'+#0-10.5b', 3)) + end) + it('errors out when %b modifier is used for a list', function() + eq('Vim(call):E745: Using a List as a Number', exc_exec('call printf("%b", [])')) + end) + it('errors out when %b modifier is used for a float', function() + eq('Vim(call):E805: Using a Float as a Number', exc_exec('call printf("%b", 3.1415926535)')) + end) +end) diff --git a/test/functional/eval/reltime_spec.lua b/test/functional/eval/reltime_spec.lua new file mode 100644 index 0000000000..da55a3fac3 --- /dev/null +++ b/test/functional/eval/reltime_spec.lua @@ -0,0 +1,36 @@ +local helpers = require('test.functional.helpers') +local clear, eq, ok = helpers.clear, helpers.eq, helpers.ok +local neq, execute, funcs = helpers.neq, helpers.execute, helpers.funcs +local reltime, reltimestr, reltimefloat = funcs.reltime, funcs.reltimestr, funcs.reltimefloat + +describe('reltimestr(), reltimefloat()', function() + before_each(clear) + + it('Checks', function() + local now = reltime() + execute('sleep 10m') + local later = reltime() + local elapsed = reltime(now) + + neq(reltimestr(elapsed), '0.0') + ok(reltimefloat(elapsed) > 0.0) + -- original vim test for < 0.1, but easily fails on travis + ok(nil ~= string.match(reltimestr(elapsed), "0%.")) + ok(reltimefloat(elapsed) < 1.0) + + local same = reltime(now, now) + local samestr = string.gsub(reltimestr(same), ' ', '') + samestr = string.sub(samestr, 1, 5) + + eq('0.000', samestr) + eq(0.0, reltimefloat(same)) + + local differs = reltime(now, later) + neq(reltimestr(differs), '0.0') + ok(reltimefloat(differs) > 0.0) + -- original vim test for < 0.1, but easily fails on travis + ok(nil ~= string.match(reltimestr(differs), "0%.")) + ok(reltimefloat(differs) < 1.0) + + end) +end) diff --git a/test/functional/server/server_spec.lua b/test/functional/eval/server_spec.lua index 1cb3c879b9..7f53522c08 100644 --- a/test/functional/server/server_spec.lua +++ b/test/functional/eval/server_spec.lua @@ -1,8 +1,8 @@ local helpers = require('test.functional.helpers') -local nvim, eq, neq, ok, eval - = helpers.nvim, helpers.eq, helpers.neq, helpers.ok, helpers.eval -local clear = helpers.clear +local nvim, eq, neq, eval = helpers.nvim, helpers.eq, helpers.neq, helpers.eval +local clear, funcs, meths = helpers.clear, helpers.funcs, helpers.meths +local os_name = helpers.os_name describe('serverstart(), serverstop()', function() before_each(clear) @@ -18,29 +18,32 @@ describe('serverstart(), serverstop()', function() eq('', eval('$NVIM_LISTEN_ADDRESS')) end) - it([[sets v:servername _only_ on nvim startup - (unless all servers are stopped)]], function() - local initial_server = eval('v:servername') + it('sets v:servername _only_ on nvim startup unless all servers are stopped', + function() + local initial_server = meths.get_vvar('servername') assert(initial_server ~= nil and initial_server:len() > 0, - "v:servername was not initialized") + 'v:servername was not initialized') -- v:servername is readonly so we cannot unset it--but we can test that it -- does not get set again thereafter. - local s = eval('serverstart()') + local s = funcs.serverstart() assert(s ~= nil and s:len() > 0, "serverstart() returned empty") neq(initial_server, s) -- serverstop() does _not_ modify v:servername... - nvim('command', "call serverstop('"..s.."')") - eq(initial_server, eval('v:servername')) + funcs.serverstop(s) + eq(initial_server, meths.get_vvar('servername')) -- ...unless we stop _all_ servers. - nvim('command', "call serverstop(serverlist()[0])") - eq('', eval('v:servername')) + funcs.serverstop(funcs.serverlist()[1]) + eq('', meths.get_vvar('servername')) -- v:servername will take the next available server. - nvim('command', "call serverstart('test_server_socket')") - eq('test_server_socket', eval('v:servername')) + local servername = (os_name() == 'windows' + and [[\\.\pipe\Xtest-functional-server-server-pipe]] + or 'Xtest-functional-server-server-socket') + funcs.serverstart(servername) + eq(servername, meths.get_vvar('servername')) end) it('serverstop() ignores invalid input', function() diff --git a/test/functional/eval/special_vars_spec.lua b/test/functional/eval/special_vars_spec.lua new file mode 100644 index 0000000000..2526483830 --- /dev/null +++ b/test/functional/eval/special_vars_spec.lua @@ -0,0 +1,171 @@ +local helpers = require('test.functional.helpers') +local exc_exec = helpers.exc_exec +local execute = helpers.execute +local funcs = helpers.funcs +local clear = helpers.clear +local eval = helpers.eval +local eq = helpers.eq +local meths = helpers.meths +local NIL = helpers.NIL + +describe('Special values', function() + before_each(clear) + + it('do not cause error when freed', function() + execute([[ + function Test() + try + return v:true + finally + return 'something else' + endtry + endfunction + ]]) + eq(0, exc_exec('call Test()')) + end) + + it('work with empty()', function() + eq(0, funcs.empty(true)) + eq(1, funcs.empty(false)) + eq(1, funcs.empty(NIL)) + end) + + it('can be stringified and eval’ed back', function() + eq(true, funcs.eval(funcs.string(true))) + eq(false, funcs.eval(funcs.string(false))) + eq(NIL, funcs.eval(funcs.string(NIL))) + end) + + it('work with is/isnot properly', function() + eq(1, eval('v:null is v:null')) + eq(0, eval('v:null is v:true')) + eq(0, eval('v:null is v:false')) + eq(1, eval('v:true is v:true')) + eq(0, eval('v:true is v:false')) + eq(1, eval('v:false is v:false')) + + eq(0, eval('v:null is 0')) + eq(0, eval('v:true is 0')) + eq(0, eval('v:false is 0')) + + eq(0, eval('v:null is 1')) + eq(0, eval('v:true is 1')) + eq(0, eval('v:false is 1')) + + eq(0, eval('v:null is ""')) + eq(0, eval('v:true is ""')) + eq(0, eval('v:false is ""')) + + eq(0, eval('v:null is "null"')) + eq(0, eval('v:true is "true"')) + eq(0, eval('v:false is "false"')) + + eq(0, eval('v:null is []')) + eq(0, eval('v:true is []')) + eq(0, eval('v:false is []')) + + eq(0, eval('v:null isnot v:null')) + eq(1, eval('v:null isnot v:true')) + eq(1, eval('v:null isnot v:false')) + eq(0, eval('v:true isnot v:true')) + eq(1, eval('v:true isnot v:false')) + eq(0, eval('v:false isnot v:false')) + + eq(1, eval('v:null isnot 0')) + eq(1, eval('v:true isnot 0')) + eq(1, eval('v:false isnot 0')) + + eq(1, eval('v:null isnot 1')) + eq(1, eval('v:true isnot 1')) + eq(1, eval('v:false isnot 1')) + + eq(1, eval('v:null isnot ""')) + eq(1, eval('v:true isnot ""')) + eq(1, eval('v:false isnot ""')) + + eq(1, eval('v:null isnot "null"')) + eq(1, eval('v:true isnot "true"')) + eq(1, eval('v:false isnot "false"')) + + eq(1, eval('v:null isnot []')) + eq(1, eval('v:true isnot []')) + eq(1, eval('v:false isnot []')) + end) + + it('work with +/-/* properly', function() + eq(1, eval('0 + v:true')) + eq(0, eval('0 + v:null')) + eq(0, eval('0 + v:false')) + + eq(-1, eval('0 - v:true')) + eq( 0, eval('0 - v:null')) + eq( 0, eval('0 - v:false')) + + eq(1, eval('1 * v:true')) + eq(0, eval('1 * v:null')) + eq(0, eval('1 * v:false')) + end) + + it('does not work with +=/-=/.=', function() + meths.set_var('true', true) + meths.set_var('false', false) + execute('let null = v:null') + + eq('Vim(let):E734: Wrong variable type for +=', exc_exec('let true += 1')) + eq('Vim(let):E734: Wrong variable type for +=', exc_exec('let false += 1')) + eq('Vim(let):E734: Wrong variable type for +=', exc_exec('let null += 1')) + + eq('Vim(let):E734: Wrong variable type for -=', exc_exec('let true -= 1')) + eq('Vim(let):E734: Wrong variable type for -=', exc_exec('let false -= 1')) + eq('Vim(let):E734: Wrong variable type for -=', exc_exec('let null -= 1')) + + eq('Vim(let):E734: Wrong variable type for .=', exc_exec('let true .= 1')) + eq('Vim(let):E734: Wrong variable type for .=', exc_exec('let false .= 1')) + eq('Vim(let):E734: Wrong variable type for .=', exc_exec('let null .= 1')) + end) + + it('work with . (concat) properly', function() + eq("true", eval('"" . v:true')) + eq("null", eval('"" . v:null')) + eq("false", eval('"" . v:false')) + end) + + it('work with type()', function() + eq(6, funcs.type(true)) + eq(6, funcs.type(false)) + eq(7, funcs.type(NIL)) + end) + + it('work with copy() and deepcopy()', function() + eq(true, funcs.deepcopy(true)) + eq(false, funcs.deepcopy(false)) + eq(NIL, funcs.deepcopy(NIL)) + + eq(true, funcs.copy(true)) + eq(false, funcs.copy(false)) + eq(NIL, funcs.copy(NIL)) + end) + + it('fails in index', function() + eq('Vim(echo):E909: Cannot index a special variable', exc_exec('echo v:true[0]')) + eq('Vim(echo):E909: Cannot index a special variable', exc_exec('echo v:false[0]')) + eq('Vim(echo):E909: Cannot index a special variable', exc_exec('echo v:null[0]')) + end) + + it('is accepted by assert_true and assert_false', function() + funcs.assert_false(false) + funcs.assert_false(true) + funcs.assert_false(NIL) + + funcs.assert_true(false) + funcs.assert_true(true) + funcs.assert_true(NIL) + + eq({ + 'Expected False but got v:true', + 'Expected False but got v:null', + 'Expected True but got v:false', + 'Expected True but got v:null', + }, meths.get_vvar('errors')) + end) +end) diff --git a/test/functional/eval/string_spec.lua b/test/functional/eval/string_spec.lua new file mode 100644 index 0000000000..abda2c59cb --- /dev/null +++ b/test/functional/eval/string_spec.lua @@ -0,0 +1,197 @@ +local helpers = require('test.functional.helpers') +local clear = helpers.clear +local eq = helpers.eq +local command = helpers.command +local meths = helpers.meths +local eval = helpers.eval +local exc_exec = helpers.exc_exec +local redir_exec = helpers.redir_exec +local funcs = helpers.funcs +local write_file = helpers.write_file +local NIL = helpers.NIL + +describe('string() function', function() + before_each(clear) + + describe('used to represent floating-point values', function() + it('dumps NaN values', function() + eq('str2float(\'nan\')', eval('string(str2float(\'nan\'))')) + end) + + it('dumps infinite values', function() + eq('str2float(\'inf\')', eval('string(str2float(\'inf\'))')) + eq('-str2float(\'inf\')', eval('string(str2float(\'-inf\'))')) + end) + + it('dumps regular values', function() + eq('1.5', funcs.string(1.5)) + eq('1.56e-20', funcs.string(1.56000e-020)) + eq('0.0', eval('string(0.0)')) + end) + + it('dumps special v: values', function() + eq('v:true', eval('string(v:true)')) + eq('v:false', eval('string(v:false)')) + eq('v:null', eval('string(v:null)')) + eq('v:true', funcs.string(true)) + eq('v:false', funcs.string(false)) + eq('v:null', funcs.string(NIL)) + end) + + it('dumps values with at most six digits after the decimal point', + function() + eq('1.234568e-20', funcs.string(1.23456789123456789123456789e-020)) + eq('1.234568', funcs.string(1.23456789123456789123456789)) + end) + + it('dumps values with at most seven digits before the decimal point', + function() + eq('1234567.891235', funcs.string(1234567.89123456789123456789)) + eq('1.234568e7', funcs.string(12345678.9123456789123456789)) + end) + + it('dumps negative values', function() + eq('-1.5', funcs.string(-1.5)) + eq('-1.56e-20', funcs.string(-1.56000e-020)) + eq('-1.234568e-20', funcs.string(-1.23456789123456789123456789e-020)) + eq('-1.234568', funcs.string(-1.23456789123456789123456789)) + eq('-1234567.891235', funcs.string(-1234567.89123456789123456789)) + eq('-1.234568e7', funcs.string(-12345678.9123456789123456789)) + end) + end) + + describe('used to represent numbers', function() + it('dumps regular values', function() + eq('0', funcs.string(0)) + eq('-1', funcs.string(-1)) + eq('1', funcs.string(1)) + end) + + it('dumps large values', function() + eq('2147483647', funcs.string(2^31-1)) + eq('-2147483648', funcs.string(-2^31)) + end) + end) + + describe('used to represent strings', function() + it('dumps regular strings', function() + eq('\'test\'', funcs.string('test')) + end) + + it('dumps empty strings', function() + eq('\'\'', funcs.string('')) + end) + + it('dumps strings with \' inside', function() + eq('\'\'\'\'\'\'\'\'', funcs.string('\'\'\'')) + eq('\'a\'\'b\'\'\'\'\'', funcs.string('a\'b\'\'')) + eq('\'\'\'b\'\'\'\'d\'', funcs.string('\'b\'\'d')) + eq('\'a\'\'b\'\'c\'\'d\'', funcs.string('a\'b\'c\'d')) + end) + + it('dumps NULL strings', function() + eq('\'\'', eval('string($XXX_UNEXISTENT_VAR_XXX)')) + end) + + it('dumps NULL lists', function() + eq('[]', eval('string(v:_null_list)')) + end) + + it('dumps NULL dictionaries', function() + eq('{}', eval('string(v:_null_dict)')) + end) + end) + + describe('used to represent funcrefs', function() + local fname = 'Xtest-functional-eval-string_spec-fref-script.vim' + + before_each(function() + write_file(fname, [[ + function Test1() + endfunction + + function s:Test2() + endfunction + + function g:Test3() + endfunction + + let g:Test2_f = function('s:Test2') + ]]) + command('source ' .. fname) + end) + + after_each(function() + os.remove(fname) + end) + + it('dumps references to built-in functions', function() + eq('function(\'function\')', eval('string(function("function"))')) + end) + + it('dumps references to user functions', function() + eq('function(\'Test1\')', eval('string(function("Test1"))')) + eq('function(\'g:Test3\')', eval('string(function("g:Test3"))')) + end) + + it('dumps references to script functions', function() + eq('function(\'<SNR>1_Test2\')', eval('string(Test2_f)')) + end) + end) + + describe('used to represent lists', function() + it('dumps empty list', function() + eq('[]', funcs.string({})) + end) + + it('dumps nested lists', function() + eq('[[[[[]]]]]', funcs.string({{{{{}}}}})) + end) + + it('dumps nested non-empty lists', function() + eq('[1, [[3, [[5], 4]], 2]]', funcs.string({1, {{3, {{5}, 4}}, 2}})) + end) + + it('errors when dumping recursive lists', function() + meths.set_var('l', {}) + eval('add(l, l)') + eq('Vim(echo):E724: unable to correctly dump variable with self-referencing container', + exc_exec('echo string(l)')) + end) + + it('dumps recursive lists despite the error', function() + meths.set_var('l', {}) + eval('add(l, l)') + eq('\nE724: unable to correctly dump variable with self-referencing container\n[{E724@0}]', + redir_exec('echo string(l)')) + eq('\nE724: unable to correctly dump variable with self-referencing container\n[[{E724@1}]]', + redir_exec('echo string([l])')) + end) + end) + + describe('used to represent dictionaries', function() + it('dumps empty dictionary', function() + eq('{}', eval('string({})')) + end) + + it('dumps non-empty dictionary', function() + eq('{\'t\'\'est\': 1}', funcs.string({['t\'est']=1})) + end) + + it('errors when dumping recursive dictionaries', function() + meths.set_var('d', {d=1}) + eval('extend(d, {"d": d})') + eq('Vim(echo):E724: unable to correctly dump variable with self-referencing container', + exc_exec('echo string(d)')) + end) + + it('dumps recursive dictionaries despite the error', function() + meths.set_var('d', {d=1}) + eval('extend(d, {"d": d})') + eq('\nE724: unable to correctly dump variable with self-referencing container\n{\'d\': {E724@0}}', + redir_exec('echo string(d)')) + eq('\nE724: unable to correctly dump variable with self-referencing container\n{\'out\': {\'d\': {E724@1}}}', + redir_exec('echo string({"out": d})')) + end) + end) +end) diff --git a/test/functional/eval/vvar_event_spec.lua b/test/functional/eval/vvar_event_spec.lua new file mode 100644 index 0000000000..bbac86524f --- /dev/null +++ b/test/functional/eval/vvar_event_spec.lua @@ -0,0 +1,15 @@ +local helpers = require('test.functional.helpers') +local clear, eval, eq = helpers.clear, helpers.eval, helpers.eq +local command = helpers.command +describe('v:event', function() + before_each(clear) + it('is empty before any autocommand', function() + eq({}, eval('v:event')) + end) + + it('is immutable', function() + eq(false, pcall(command, 'let v:event = {}')) + eq(false, pcall(command, 'let v:event.mykey = {}')) + end) +end) + diff --git a/test/functional/ex_cmds/append_spec.lua b/test/functional/ex_cmds/append_spec.lua new file mode 100644 index 0000000000..2d5ab8e8c8 --- /dev/null +++ b/test/functional/ex_cmds/append_spec.lua @@ -0,0 +1,54 @@ +local helpers = require('test.functional.helpers') + +local eq = helpers.eq +local feed = helpers.feed +local clear = helpers.clear +local funcs = helpers.funcs +local command = helpers.command +local curbufmeths = helpers.curbufmeths + +before_each(function() + clear() + curbufmeths.set_lines(0, 1, true, { 'foo', 'bar', 'baz' }) +end) + +local buffer_contents = function() + return curbufmeths.get_lines(0, -1, false) +end + +local cmdtest = function(cmd, prep, ret1) + describe(':' .. cmd, function() + it(cmd .. 's' .. prep .. ' the current line by default', function() + 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 + -- crashed. + it(cmd .. 's' .. prep .. ' the current line by default when feeding', + function() + feed(':' .. cmd .. '\nabc\ndef\n.\n') + eq(ret1, buffer_contents()) + end) + -- This used to crash since that commit as well. + it('opens empty cmdline window', function() + local hisline = '" Some comment to be stored in history' + feed(':' .. hisline .. '<CR>') + feed(':' .. cmd .. '<CR>abc<CR>def<C-f>') + eq({ 'def' }, buffer_contents()) + eq(hisline, funcs.histget(':', -2)) + eq(cmd, funcs.histget(':')) + -- Test that command-line window was launched + eq('nofile', curbufmeths.get_option('buftype')) + eq('n', funcs.mode(1)) + feed('<CR>') + eq('c', funcs.mode(1)) + feed('.<CR>') + eq('n', funcs.mode(1)) + eq(ret1, buffer_contents()) + end) + end) +end +cmdtest('insert', ' before', { 'abc', 'def', 'foo', 'bar', 'baz' }) +cmdtest('append', ' after', { 'foo', 'abc', 'def', 'bar', 'baz' }) +cmdtest('change', '', { 'abc', 'def', 'bar', 'baz' }) diff --git a/test/functional/ex_cmds/cd_spec.lua b/test/functional/ex_cmds/cd_spec.lua new file mode 100644 index 0000000000..69467632a4 --- /dev/null +++ b/test/functional/ex_cmds/cd_spec.lua @@ -0,0 +1,152 @@ +-- 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') + +-- 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 +} + +-- 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 + +-- 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 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() + before_each(function() + clear() + for _, d in ipairs(directories) do + lfs.mkdir(d) + end + end) + + after_each(function() + for _, d in ipairs(directories) do + lfs.rmdir(d) + end + end) + + it('works', function() + -- Store the initial working directory + local globalDir = cwd() + + -- Create a new tab first and verify that is has the same working dir + execute('tabnew') + eq(globalDir, cwd()) + eq(globalDir, tcwd()) -- has no tab-local directory + eq(0, tlwd()) + eq(globalDir, wcwd()) -- has no window-local directory + eq(0, wlwd()) + + -- Change tab-local working directory and verify it is different + execute('silent t' .. cmd .. ' ' .. directories[1]) + eq(globalDir .. '/' .. directories[1], cwd()) + eq(cwd(), tcwd()) -- working directory maches tab directory + eq(1, tlwd()) + eq(cwd(), wcwd()) -- still no window-directory + eq(0, wlwd()) + + -- Create a new window in this tab to test `:lcd` + 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(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(0, wlwd()) -- No window-local directory + + -- Change back to initial tab and verify working directory has stayed + execute('tabnext') + eq(globalDir, cwd() ) + eq(0, tlwd()) + eq(0, wlwd()) + + -- Verify global changes don't affect local ones + execute('silent ' .. cmd .. ' ' .. directories[3]) + eq(globalDir .. '/' .. directories[3], cwd()) + execute('tabnext') + eq(globalDir .. '/' .. directories[1], cwd()) + eq(globalDir .. '/' .. directories[1], tcwd()) + eq(0, wlwd()) -- Still no window-local directory in this window + + -- Unless the global change happened in a tab with local directory + execute('silent ' .. cmd .. ' ..') + eq(globalDir, cwd() ) + eq(0 , tlwd()) + eq(0 , wlwd()) + -- Which also affects the first tab + execute('tabnext') + eq(globalDir, cwd()) + + -- But not in a window with its own local directory + execute('tabnext | wincmd w') + eq(globalDir .. '/' .. directories[2], cwd() ) + eq(0 , tlwd()) + eq(globalDir .. '/' .. directories[2], wcwd()) + end) + end) +end + +-- Test legal parameters for 'getcwd' and 'haslocaldir' +for _, cmd in ipairs {'getcwd', 'haslocaldir'} do + describe(cmd..'()', function() + before_each(function() + clear() + end) + + -- Test invalid argument types + local err474 = 'Vim(call):E474: Invalid argument' + it('fails on string', function() + eq(err474, exc_exec('call ' .. cmd .. '("some string")')) + end) + it('fails on float', function() + eq(err474, exc_exec('call ' .. cmd .. '(1.0)')) + end) + it('fails on list', function() + eq(err474, exc_exec('call ' .. cmd .. '([1, 2])')) + end) + it('fails on dictionary', function() + eq(err474, exc_exec('call ' .. cmd .. '({"key": "value"})')) + end) + it('fails on funcref', function() + eq(err474, exc_exec('call ' .. cmd .. '(function("tr"))')) + end) + + -- Test invalid numbers + it('fails on number less than -1', function() + eq(err474, exc_exec('call ' .. cmd .. '(-2)')) + end) + local err5001 = 'Vim(call):E5001: Higher scope cannot be -1 if lower scope is >= 0.' + it('fails on -1 if previous arg is >=0', function() + eq(err5001, exc_exec('call ' .. cmd .. '(0, -1)')) + end) + + -- Test wrong number of arguments + local err118 = 'Vim(call):E118: Too many arguments for function: ' .. cmd + it('fails to parse more than one argument', function() + eq(err118, exc_exec('call ' .. cmd .. '(0, 0, 0)')) + end) + end) +end + diff --git a/test/functional/ex_cmds/encoding_spec.lua b/test/functional/ex_cmds/encoding_spec.lua index 997776ca25..6d402b7974 100644 --- a/test/functional/ex_cmds/encoding_spec.lua +++ b/test/functional/ex_cmds/encoding_spec.lua @@ -21,20 +21,21 @@ describe('&encoding', function() 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' when enc&vi is 'utf-8', 'utf-8' otherwise - execute('set fenc=default') - local enc_default, enc_other, width = eval('&fenc'), 'utf-8', 3 - if enc_default == 'utf-8' then - enc_other = 'latin1' - width = 4 -- utf-8 string 'Bär' will count as 4 latin1 chars - end + it('can be changed before startup', function() + clear('set enc=latin1') + execute('set encoding=utf-8') + -- error message expected + feed('<cr>') + eq('latin1', eval('&encoding')) + eq(4, eval('strwidth("Bär")')) + end) - clear('set enc=' .. enc_other) + 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(enc_other, eval('&encoding')) - eq(width, eval('strwidth("Bär")')) + eq('latin1', eval('&encoding')) + eq(4, eval('strwidth("Bär")')) end) end) diff --git a/test/functional/ex_cmds/grep_spec.lua b/test/functional/ex_cmds/grep_spec.lua index 9c792099c1..f3ff0a3817 100644 --- a/test/functional/ex_cmds/grep_spec.lua +++ b/test/functional/ex_cmds/grep_spec.lua @@ -1,7 +1,6 @@ local helpers = require('test.functional.helpers') -local clear, execute, nvim, feed, eq, ok, eval = - helpers.clear, helpers.execute, helpers.nvim, helpers.feed, - helpers.eq, helpers.ok, helpers.eval +local clear, execute, feed, ok, eval = + helpers.clear, helpers.execute, helpers.feed, helpers.ok, helpers.eval describe(':grep', function() before_each(clear) diff --git a/test/functional/ex_cmds/oldfiles_spec.lua b/test/functional/ex_cmds/oldfiles_spec.lua new file mode 100644 index 0000000000..5bba1a0e7c --- /dev/null +++ b/test/functional/ex_cmds/oldfiles_spec.lua @@ -0,0 +1,98 @@ +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_prog, wait = helpers.feed, helpers.nvim_prog, helpers.wait +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() + 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() + local 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) + wait() + _clear() + execute('rshada! ' .. shada_file) + + -- 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. + 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..744b22621f --- /dev/null +++ b/test/functional/ex_cmds/profile_spec.lua @@ -0,0 +1,51 @@ +require('os') +local lfs = 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/quit_spec.lua b/test/functional/ex_cmds/quit_spec.lua index 3cd8e19617..a8156228d3 100644 --- a/test/functional/ex_cmds/quit_spec.lua +++ b/test/functional/ex_cmds/quit_spec.lua @@ -1,5 +1,5 @@ local helpers = require('test.functional.helpers') -local execute, eq, clear = helpers.execute, helpers.eq, helpers.clear +local clear = helpers.clear describe(':qa', function() before_each(function() diff --git a/test/functional/ex_cmds/recover_spec.lua b/test/functional/ex_cmds/recover_spec.lua index 6749cfaf40..e1d01f6896 100644 --- a/test/functional/ex_cmds/recover_spec.lua +++ b/test/functional/ex_cmds/recover_spec.lua @@ -1,6 +1,7 @@ -- Tests for :recover local helpers = require('test.functional.helpers') +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 diff --git a/test/functional/ex_cmds/sign_spec.lua b/test/functional/ex_cmds/sign_spec.lua index be213cd0d9..c50704504d 100644 --- a/test/functional/ex_cmds/sign_spec.lua +++ b/test/functional/ex_cmds/sign_spec.lua @@ -1,7 +1,5 @@ local helpers = require('test.functional.helpers') -local clear, nvim, buffer, curbuf, curwin, eq, ok = - helpers.clear, helpers.nvim, helpers.buffer, helpers.curbuf, helpers.curwin, - helpers.eq, helpers.ok +local clear, nvim, eq = helpers.clear, helpers.nvim, helpers.eq describe('sign', function() before_each(clear) diff --git a/test/functional/ex_cmds/write_spec.lua b/test/functional/ex_cmds/write_spec.lua new file mode 100644 index 0000000000..d90b297ca8 --- /dev/null +++ b/test/functional/ex_cmds/write_spec.lua @@ -0,0 +1,38 @@ +-- Specs for :write + +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 + +describe(':write', function() + it('&backupcopy=auto preserves symlinks', function() + clear('set backupcopy=auto') + os.remove('test_bkc_file.txt') + os.remove('test_bkc_link.txt') + write_file('test_bkc_file.txt', 'content0') + execute("silent !ln -s test_bkc_file.txt test_bkc_link.txt") + source([[ + edit test_bkc_link.txt + call setline(1, ['content1']) + write + ]]) + eq(eval("['content1']"), eval("readfile('test_bkc_file.txt')")) + eq(eval("['content1']"), eval("readfile('test_bkc_link.txt')")) + 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') + write_file('test_bkc_file.txt', 'content0') + execute("silent !ln -s test_bkc_file.txt test_bkc_link.txt") + source([[ + edit test_bkc_link.txt + call setline(1, ['content1']) + write + ]]) + eq(eval("['content0']"), eval("readfile('test_bkc_file.txt')")) + eq(eval("['content1']"), eval("readfile('test_bkc_link.txt')")) + end) +end) diff --git a/test/functional/ex_cmds/wundo_spec.lua b/test/functional/ex_cmds/wundo_spec.lua index d8bd8a7031..969dfea3d9 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:close() + end) end) diff --git a/test/functional/ex_cmds/wviminfo_spec.lua b/test/functional/ex_cmds/wviminfo_spec.lua index b6b9185cf2..21f14be62c 100644 --- a/test/functional/ex_cmds/wviminfo_spec.lua +++ b/test/functional/ex_cmds/wviminfo_spec.lua @@ -1,6 +1,6 @@ local helpers, lfs = require('test.functional.helpers'), require('lfs') -local clear, execute, eq, neq, spawn, nvim_prog, set_session, wait, write_file - = helpers.clear, helpers.execute, helpers.eq, helpers.neq, helpers.spawn, +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 describe(':wshada', function() @@ -9,11 +9,11 @@ describe(':wshada', function() before_each(function() if session then - session:exit(0) + session:close() end -- Override the default session because we need 'swapfile' for these tests. - local session = spawn({nvim_prog, '-u', 'NONE', '-i', '/dev/null', '--embed', + session = spawn({nvim_prog, '-u', 'NONE', '-i', '/dev/null', '--embed', '--cmd', 'set swapfile'}) set_session(session) diff --git a/test/functional/fixtures/shell-test.c b/test/functional/fixtures/shell-test.c index 5fa8a58049..d9ec254aff 100644 --- a/test/functional/fixtures/shell-test.c +++ b/test/functional/fixtures/shell-test.c @@ -1,25 +1,59 @@ -// A simple implementation of a shell for testing -// `termopen([&sh, &shcf, '{cmd'}])` and `termopen([&sh])`. -// -// If launched with no arguments, prints "ready $ ", otherwise prints -// "ready $ {cmd}\n". - #include <stdio.h> #include <string.h> +#include <stdint.h> + +static void help(void) +{ + puts("A simple implementation of a shell for testing termopen()."); + puts(""); + puts("Usage:"); + puts(" shell-test --help"); + puts(" Prints this help to stdout."); + puts(" shell-test"); + puts(" shell-test EXE"); + puts(" Prints \"ready $ \" to stderr."); + puts(" shell-test EXE \"prog args...\""); + puts(" Prints \"ready $ prog args...\\n\" to stderr."); + puts(" shell-test REP {byte} \"line line line\""); + puts(" Prints \"{lnr}: line line line\\n\" to stdout {byte} times."); + puts(" I.e. for `shell-test REP ab \"test\"'"); + puts(" 0: test"); + puts(" ..."); + puts(" 96: test"); + puts(" will be printed because byte `a' is equal to 97."); +} int main(int argc, char **argv) { - fprintf(stderr, "ready $ "); + if (argc == 2 && strcmp(argv[1], "--help") == 0) { + help(); + } - if (argc == 3) { - // argv should be {"terminal-test", "EXE", "prog args..."} - if (strcmp(argv[1], "EXE") != 0) { - fprintf(stderr, "first argument must be 'EXE'\n"); - return 2; + if (argc >= 2) { + if (strcmp(argv[1], "EXE") == 0) { + fprintf(stderr, "ready $ "); + if (argc >= 3) { + fprintf(stderr, "%s\n", argv[2]); + } + } else if (strcmp(argv[1], "REP") == 0) { + if (argc < 4) { + fprintf(stderr, "Not enough REP arguments\n"); + return 4; + } + uint8_t number = (uint8_t) *argv[2]; + for (uint8_t i = 0; i < number; i++) { + printf("%d: %s\n", (int) i, argv[3]); + } + } else { + fprintf(stderr, "Unknown first argument\n"); + return 3; } - - fprintf(stderr, "%s\n", argv[2]); + return 0; + } else if (argc == 1) { + fprintf(stderr, "ready $ "); + return 0; + } else { + fprintf(stderr, "Missing first argument\n"); + return 2; } - - return 0; } diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index 8a85f187cf..37b7bf664c 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -1,15 +1,16 @@ require('coxpcall') +local lfs = require('lfs') local assert = require('luassert') -local Loop = require('nvim.loop') -local MsgpackStream = require('nvim.msgpack_stream') -local AsyncSession = require('nvim.async_session') +local ChildProcessStream = require('nvim.child_process_stream') local Session = require('nvim.session') local nvim_prog = os.getenv('NVIM_PROG') or 'build/bin/nvim' local nvim_argv = {nvim_prog, '-u', 'NONE', '-i', 'NONE', '-N', - '--cmd', 'set shortmess+=I background=light noswapfile noautoindent laststatus=1 encoding=utf-8 undodir=. directory=. viewdir=. backupdir=.', + '--cmd', 'set shortmess+=I background=light noswapfile noautoindent laststatus=1 undodir=. directory=. viewdir=. backupdir=.', '--embed'} +local mpack = require('mpack') + -- Formulate a path to the directory containing nvim. We use this to -- help run test executables. It helps to keep the tests working, even -- when the build is not in the default location. @@ -55,9 +56,12 @@ if prepend_argv then nvim_argv = new_nvim_argv end -local session, loop_running, loop_stopped, last_error +local session, loop_running, last_error local function set_session(s) + if session then + session:close() + end session = s end @@ -79,7 +83,7 @@ local function next_message() end local function call_and_stop_on_error(...) - local status, result = copcall(...) + local status, result = copcall(...) -- luacheck: ignore if not status then session:stop() last_error = result @@ -109,7 +113,6 @@ local function run(request_cb, notification_cb, setup_cb, timeout) end end - loop_stopped = false loop_running = true session:run(on_request, on_notification, on_setup, timeout) loop_running = false @@ -121,7 +124,6 @@ local function run(request_cb, notification_cb, setup_cb, timeout) end local function stop() - loop_stopped = true session:stop() end @@ -133,6 +135,22 @@ local function nvim_eval(expr) return request('vim_eval', expr) end +local os_name = (function() + local name = nil + return (function() + if not name then + if nvim_eval('has("win32")') == 1 then + name = 'windows' + elseif nvim_eval('has("macunix")') == 1 then + name = 'osx' + else + name = 'unix' + end + end + return name + end) +end)() + local function nvim_call(name, ...) return request('vim_call_function', name, {...}) end @@ -158,7 +176,7 @@ local function dedent(str) return str end -- create a pattern for the indent - indent = indent:gsub('%s', '%%s') + indent = indent:gsub('%s', '[ \t]') -- strip it from the first line str = str:gsub('^'..indent, '') -- strip it from the remaining lines @@ -194,24 +212,17 @@ local function merge_args(...) end local function spawn(argv, merge) - local loop = Loop.new() - local msgpack_stream = MsgpackStream.new(loop) - local async_session = AsyncSession.new(msgpack_stream) - local session = Session.new(async_session) - loop:spawn(merge and merge_args(prepend_argv, argv) or argv) - return session + local child_stream = ChildProcessStream.spawn(merge and merge_args(prepend_argv, argv) or argv) + return Session.new(child_stream) end local function clear(extra_cmd) - if session then - session:exit(0) - end local args = {unpack(nvim_argv)} if extra_cmd ~= nil then table.insert(args, '--cmd') table.insert(args, extra_cmd) end - session = spawn(args) + set_session(spawn(args)) end local function insert(...) @@ -247,9 +258,13 @@ end local function source(code) local tmpname = os.tmpname() + if os_name() == 'osx' and string.match(tmpname, '^/tmp') then + tmpname = '/private'..tmpname + end write_file(tmpname, code) nvim_command('source '..tmpname) os.remove(tmpname) + return tmpname end local function eq(expected, actual) @@ -301,7 +316,7 @@ local function curbuf_contents() -- previously sent keys are processed(vim_eval is a deferred function, and -- only processed after all input) wait() - return table.concat(curbuf('get_line_slice', 0, -1, true, true), '\n') + return table.concat(curbuf('get_lines', 0, -1, true), '\n') end local function curwin(method, ...) @@ -329,17 +344,15 @@ local function rmdir(path) return nil end for file in lfs.dir(path) do - if file == '.' or file == '..' then - goto continue - end - ret, err = os.remove(path..'/'..file) - if not ret then - error('os.remove: '..err) - return nil + if file ~= '.' and file ~= '..' then + local ret, err = os.remove(path..'/'..file) + if not ret then + error('os.remove: '..err) + return nil + end end - ::continue:: end - ret, err = os.remove(path) + local ret, err = os.remove(path) if not ret then error('os.remove: '..err) end @@ -371,15 +384,15 @@ local function redir_exec(cmd) end local function create_callindex(func) - local tbl = {} - setmetatable(tbl, { + local table = {} + setmetatable(table, { __index = function(tbl, arg1) - ret = function(...) return func(arg1, ...) end + local ret = function(...) return func(arg1, ...) end tbl[arg1] = ret return ret end, }) - return tbl + return table end local funcs = create_callindex(nvim_call) @@ -426,6 +439,7 @@ return { wait = wait, set_session = set_session, write_file = write_file, + os_name = os_name, rmdir = rmdir, mkdir = lfs.mkdir, exc_exec = exc_exec, @@ -439,4 +453,5 @@ return { curbufmeths = curbufmeths, curwinmeths = curwinmeths, curtabmeths = curtabmeths, + NIL = mpack.NIL } diff --git a/test/functional/job/job_spec.lua b/test/functional/job/job_spec.lua index bdaf2a7ec6..d21b9051e2 100644 --- a/test/functional/job/job_spec.lua +++ b/test/functional/job/job_spec.lua @@ -1,11 +1,11 @@ local helpers = require('test.functional.helpers') -local clear, eq, eval, execute, expect, feed, insert, neq, next_msg, nvim, - nvim_dir, ok, run, session, source, stop, wait, write_file = helpers.clear, - helpers.eq, helpers.eval, helpers.execute, helpers.expect, helpers.feed, +local clear, eq, eval, execute, feed, insert, neq, next_msg, nvim, + nvim_dir, ok, source, write_file = helpers.clear, + helpers.eq, helpers.eval, helpers.execute, helpers.feed, helpers.insert, helpers.neq, helpers.next_message, helpers.nvim, - helpers.nvim_dir, helpers.ok, helpers.run, helpers.session, helpers.source, - helpers.stop, helpers.wait, helpers.write_file + helpers.nvim_dir, helpers.ok, helpers.source, + helpers.write_file local Screen = require('test.functional.ui.screen') @@ -142,6 +142,35 @@ describe('jobs', function() nvim('command', "call jobstart(['cat', '-'], g:job_opts)") end) + it('can get the pid value using getpid', function() + nvim('command', "let j = jobstart(['cat', '-'], g:job_opts)") + local pid = eval('jobpid(j)') + eq(0,os.execute('ps -p '..pid..' > /dev/null')) + nvim('command', 'call jobstop(j)') + eq({'notification', 'exit', {0, 0}}, next_msg()) + neq(0,os.execute('ps -p '..pid..' > /dev/null')) + end) + + it("doesn't survive the exit of nvim", function() + -- use sleep, which doesn't die on stdin close + nvim('command', "let j = jobstart(['sleep', '1000'], g:job_opts)") + local pid = eval('jobpid(j)') + eq(0,os.execute('ps -p '..pid..' > /dev/null')) + clear() + neq(0,os.execute('ps -p '..pid..' > /dev/null')) + end) + + it('can survive the exit of nvim with "detach"', function() + nvim('command', 'let g:job_opts.detach = 1') + nvim('command', "let j = jobstart(['sleep', '1000'], g:job_opts)") + local pid = eval('jobpid(j)') + eq(0,os.execute('ps -p '..pid..' > /dev/null')) + clear() + eq(0,os.execute('ps -p '..pid..' > /dev/null')) + -- clean up after ourselves + os.execute('kill -9 '..pid..' > /dev/null') + end) + it('can pass user data to the callback', function() nvim('command', 'let g:job_opts.user = {"n": 5, "s": "str", "l": [1]}') nvim('command', "call jobstart(['echo'], g:job_opts)") @@ -370,7 +399,7 @@ describe('jobs', function() describe('running tty-test program', function() local function next_chunk() - local rv = '' + local rv while true do local msg = next_msg() local data = msg[3][2] diff --git a/test/functional/legacy/003_cindent_spec.lua b/test/functional/legacy/003_cindent_spec.lua index 1857721563..4b838eda1d 100644 --- a/test/functional/legacy/003_cindent_spec.lua +++ b/test/functional/legacy/003_cindent_spec.lua @@ -4,7 +4,7 @@ -- in the original test. These have been converted to "it" test cases here. local helpers = require('test.functional.helpers') -local feed, insert, source = helpers.feed, helpers.insert, helpers.source +local feed, insert = helpers.feed, helpers.insert local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect -- Inserts text as usual, and additionally positions the cursor on line 1 and @@ -674,6 +674,13 @@ describe('cindent', function() { } + A::A(int a, int b) + : aa(a), + bb(b), + cc(c) + { + } + class CAbc : public BaseClass1, protected BaseClass2 @@ -901,6 +908,72 @@ describe('cindent', function() { 111111111111111111; } + void getstring() { + /* Raw strings */ + const char* s = R"( + test { + # comment + field: 123 + } + )"; + } + void getstring() { + const char* s = R"foo( + test { + # comment + field: 123 + } + )foo"; + } + + { + int a[4] = { + [0] = 0, + [1] = 1, + [2] = 2, + [3] = 3, + }; + } + + { + a = b[2] + + 3; + } + + { + if (1) + /* aaaaa + * bbbbb + */ + a = 1; + } + + void func() + { + switch (foo) + { + case (bar): + if (baz()) + quux(); + break; + case (shmoo): + if (!bar) + { + } + case (foo1): + switch (bar) + { + case baz: + baz_f(); + break; + } + break; + default: + baz(); + baz(); + break; + } + } /* end of AUTO */ ]=]) @@ -1563,6 +1636,13 @@ describe('cindent', function() { } + A::A(int a, int b) + : aa(a), + bb(b), + cc(c) + { + } + class CAbc : public BaseClass1, protected BaseClass2 @@ -1790,6 +1870,72 @@ describe('cindent', function() { 111111111111111111; } + void getstring() { + /* Raw strings */ + const char* s = R"( + test { + # comment + field: 123 + } + )"; + } + void getstring() { + const char* s = R"foo( + test { + # comment + field: 123 + } + )foo"; + } + + { + int a[4] = { + [0] = 0, + [1] = 1, + [2] = 2, + [3] = 3, + }; + } + + { + a = b[2] + + 3; + } + + { + if (1) + /* aaaaa + * bbbbb + */ + a = 1; + } + + void func() + { + switch (foo) + { + case (bar): + if (baz()) + quux(); + break; + case (shmoo): + if (!bar) + { + } + case (foo1): + switch (bar) + { + case baz: + baz_f(); + break; + } + break; + default: + baz(); + baz(); + break; + } + } /* end of AUTO */ ]=]) @@ -4213,4 +4359,321 @@ describe('cindent', function() JSEND ]=]) end) + + it('javascript indent / vim-patch 7.4.670', function() + insert_([=[ + + JSSTART + // Results of JavaScript indent + // 1 + (function(){ + var a = [ + 'a', + 'b', + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + 'i' + ]; + }()) + + // 2 + (function(){ + var a = [ + 0 + + 5 * + 9 * + 'a', + 'b', + 0 + + 5 * + 9 * + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + 'i' + ]; + }()) + + // 3 + (function(){ + var a = [ + 0 + + // comment 1 + 5 * + /* comment 2 */ + 9 * + 'a', + 'b', + 0 + + 5 * + 9 * + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + 'i' + ]; + }()) + + // 4 + { + var a = [ + 0, + 1 + ]; + var b; + var c; + } + + // 5 + { + var a = [ + [ + 0 + ], + 2, + 3 + ]; + } + + // 6 + { + var a = [ + [ + 0, + 1 + ], + 2, + 3 + ]; + } + + // 7 + { + var a = [ + // [ + 0, + // 1 + // ], + 2, + 3 + ]; + } + + // 8 + var x = [ + (function(){ + var a, + b, + c, + d, + e, + f, + g, + h, + i; + }) + ]; + + // 9 + var a = [ + 0 + + 5 * + 9 * + 'a', + 'b', + 0 + + 5 * + 9 * + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + 'i' + ]; + + // 10 + var a, + b, + c, + d, + e, + f, + g, + h, + i; + JSEND + ]=]) + + -- :set cino=j1,J1,+2 + execute('set cino=j1,J1,+2') + execute('/^JSSTART') + feed('=/^JSEND<cr>') + + expect([=[ + + JSSTART + // Results of JavaScript indent + // 1 + (function(){ + var a = [ + 'a', + 'b', + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + 'i' + ]; + }()) + + // 2 + (function(){ + var a = [ + 0 + + 5 * + 9 * + 'a', + 'b', + 0 + + 5 * + 9 * + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + 'i' + ]; + }()) + + // 3 + (function(){ + var a = [ + 0 + + // comment 1 + 5 * + /* comment 2 */ + 9 * + 'a', + 'b', + 0 + + 5 * + 9 * + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + 'i' + ]; + }()) + + // 4 + { + var a = [ + 0, + 1 + ]; + var b; + var c; + } + + // 5 + { + var a = [ + [ + 0 + ], + 2, + 3 + ]; + } + + // 6 + { + var a = [ + [ + 0, + 1 + ], + 2, + 3 + ]; + } + + // 7 + { + var a = [ + // [ + 0, + // 1 + // ], + 2, + 3 + ]; + } + + // 8 + var x = [ + (function(){ + var a, + b, + c, + d, + e, + f, + g, + h, + i; + }) + ]; + + // 9 + var a = [ + 0 + + 5 * + 9 * + 'a', + 'b', + 0 + + 5 * + 9 * + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + 'i' + ]; + + // 10 + var a, + b, + c, + d, + e, + f, + g, + h, + i; + JSEND + ]=]) + end) end) diff --git a/test/functional/legacy/009_bufleave_autocommand_spec.lua b/test/functional/legacy/009_bufleave_autocommand_spec.lua index 0fc1b5b657..8c18639c8f 100644 --- a/test/functional/legacy/009_bufleave_autocommand_spec.lua +++ b/test/functional/legacy/009_bufleave_autocommand_spec.lua @@ -1,7 +1,7 @@ -- Test for Bufleave autocommand that deletes the buffer we are about to edit. local helpers = require('test.functional.helpers') -local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert +local clear, insert = helpers.clear, helpers.insert local execute, expect = helpers.execute, helpers.expect describe('BufLeave autocommand', function() diff --git a/test/functional/legacy/011_autocommands_spec.lua b/test/functional/legacy/011_autocommands_spec.lua new file mode 100644 index 0000000000..483e465cee --- /dev/null +++ b/test/functional/legacy/011_autocommands_spec.lua @@ -0,0 +1,230 @@ +-- Tests for autocommands +-- - FileWritePre writing a compressed file +-- - FileReadPost reading a compressed file +-- - BufNewFile reading a file template +-- - BufReadPre decompressing the file to be read +-- - FilterReadPre substituting characters in the temp file +-- - FilterReadPost substituting characters after filtering +-- - FileReadPre set options for decompression +-- - FileReadPost decompress the file +-- Note: This test is skipped if "gzip" is not available. +-- $GZIP is made empty, "-v" would cause trouble. +-- Use a FileChangedShell autocommand to avoid a prompt for "Xtestfile.gz" +-- being modified outside of Vim (noticed on Solaris). + +local helpers, lfs = require('test.functional.helpers'), require('lfs') +local clear, execute, expect, eq, neq, dedent, write_file, feed = + helpers.clear, helpers.execute, helpers.expect, helpers.eq, helpers.neq, + helpers.dedent, helpers.write_file, helpers.feed + +local function has_gzip() + return os.execute('gzip --help >/dev/null 2>&1') == 0 +end + +local function prepare_gz_file(name, text) + write_file(name, text..'\n') + -- Compress the file with gzip. + os.execute('gzip --force '..name) + -- This should create the .gz file and delete the original. + neq(nil, lfs.attributes(name..'.gz')) + eq(nil, lfs.attributes(name)) +end + +describe('file reading, writing and bufnew and filter autocommands', function() + local text1 = dedent([[ + start of testfile + line 2 Abcdefghijklmnopqrstuvwxyz + line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + line 4 Abcdefghijklmnopqrstuvwxyz + line 5 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + line 6 Abcdefghijklmnopqrstuvwxyz + line 7 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + line 8 Abcdefghijklmnopqrstuvwxyz + line 9 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + line 10 Abcdefghijklmnopqrstuvwxyz + end of testfile]]) + setup(function() + write_file('Xtest.c', [[ + /* + * Here is a new .c file + */ + ]]) + end) + before_each(clear) + teardown(function() + os.remove('Xtestfile.gz') + os.remove('Xtest.c') + os.remove('test.out') + end) + + if not has_gzip() then + pending('skipped (missing `gzip` utility)', function() end) + else + + it('FileReadPost (using gzip)', function() + prepare_gz_file('Xtestfile', text1) + execute('let $GZIP = ""') + --execute('au FileChangedShell * echo "caught FileChangedShell"') + execute('set bin') + execute("au FileReadPost *.gz '[,']!gzip -d") + -- Read and decompress the testfile. + execute('$r Xtestfile.gz') + expect('\n'..text1) + end) + + it('BufReadPre, BufReadPost (using gzip)', function() + prepare_gz_file('Xtestfile', text1) + local gzip_data = io.open('Xtestfile.gz'):read('*all') + execute('let $GZIP = ""') + -- Setup autocommands to decompress before reading and re-compress afterwards. + execute("au BufReadPre *.gz exe '!gzip -d ' . shellescape(expand('<afile>'))") + execute("au BufReadPre *.gz call rename(expand('<afile>:r'), expand('<afile>'))") + execute("au BufReadPost *.gz call rename(expand('<afile>'), expand('<afile>:r'))") + execute("au BufReadPost *.gz exe '!gzip ' . shellescape(expand('<afile>:r'))") + -- Edit compressed file. + execute('e! Xtestfile.gz') + -- Discard all prompts and messages. + feed('<C-L>') + -- Expect the decompressed file in the buffer. + expect(text1) + -- Expect the original file to be unchanged. + eq(gzip_data, io.open('Xtestfile.gz'):read('*all')) + end) + + it('FileReadPre, FileReadPost', function() + prepare_gz_file('Xtestfile', text1) + execute('au! FileReadPre *.gz exe "silent !gzip -d " . shellescape(expand("<afile>"))') + execute('au FileReadPre *.gz call rename(expand("<afile>:r"), expand("<afile>"))') + execute("au! FileReadPost *.gz '[,']s/l/L/") + -- Read compressed file. + execute('$r Xtestfile.gz') + -- Discard all prompts and messages. + feed('<C-L>') + expect([[ + + start of testfiLe + Line 2 Abcdefghijklmnopqrstuvwxyz + Line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + Line 4 Abcdefghijklmnopqrstuvwxyz + Line 5 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + Line 6 Abcdefghijklmnopqrstuvwxyz + Line 7 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + Line 8 Abcdefghijklmnopqrstuvwxyz + Line 9 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + Line 10 Abcdefghijklmnopqrstuvwxyz + end of testfiLe]]) + end) + + end + + it('FileAppendPre, FileAppendPost', function() + execute('au BufNewFile *.c read Xtest.c') + -- Will load Xtest.c. + execute('e! foo.c') + execute("au FileAppendPre *.out '[,']s/new/NEW/") + execute('au FileAppendPost *.out !cat Xtest.c >>test.out') + -- Append it to the output file. + execute('w>>test.out') + -- Discard all prompts and messages. + feed('<C-L>') + -- Expect the decompressed file in the buffer. + execute('e test.out') + expect([[ + + /* + * Here is a NEW .c file + */]]) + end) + + it('FilterReadPre, FilterReadPost', function() + -- Write a special input file for this test block. + write_file('test.out', dedent([[ + startstart + ]]) .. text1 .. dedent([[ + + + start of test.c + /* + * Here is a new .c file + */ + end of test.c + ]]) .. text1 .. dedent([[ + + + /* + * Here is a NEW .c file + */ + /* + * Here is a new .c file + */ + ]]) .. text1 .. dedent([[ + + /* + * Here is a new .c file + */]])) + -- Need temp files here. + execute('set shelltemp') + execute('au FilterReadPre *.out call rename(expand("<afile>"), expand("<afile>") . ".t")') + execute('au FilterReadPre *.out exe "silent !sed s/e/E/ " . shellescape(expand("<afile>")) . ".t >" . shellescape(expand("<afile>"))') + execute('au FilterReadPre *.out exe "silent !rm " . shellescape(expand("<afile>")) . ".t"') + execute("au FilterReadPost *.out '[,']s/x/X/g") + -- Edit the output file. + execute('e! test.out') + execute('23,$!cat') + -- Discard all prompts and messages. + feed('<C-L>') + -- Remove CR for when sed adds them. + execute([[23,$s/\r$//]]) + expect([[ + startstart + start of testfile + line 2 Abcdefghijklmnopqrstuvwxyz + line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + line 4 Abcdefghijklmnopqrstuvwxyz + line 5 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + line 6 Abcdefghijklmnopqrstuvwxyz + line 7 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + line 8 Abcdefghijklmnopqrstuvwxyz + line 9 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + line 10 Abcdefghijklmnopqrstuvwxyz + end of testfile + + start of test.c + /* + * Here is a new .c file + */ + end of test.c + start of testfile + line 2 Abcdefghijklmnopqrstuvwxyz + line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + line 4 Abcdefghijklmnopqrstuvwxyz + linE 5 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + linE 6 AbcdefghijklmnopqrstuvwXyz + linE 7 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + linE 8 AbcdefghijklmnopqrstuvwXyz + linE 9 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + linE 10 AbcdefghijklmnopqrstuvwXyz + End of testfile + + /* + * HEre is a NEW .c file + */ + /* + * HEre is a new .c file + */ + start of tEstfile + linE 2 AbcdefghijklmnopqrstuvwXyz + linE 3 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + linE 4 AbcdefghijklmnopqrstuvwXyz + linE 5 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + linE 6 AbcdefghijklmnopqrstuvwXyz + linE 7 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + linE 8 AbcdefghijklmnopqrstuvwXyz + linE 9 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + linE 10 AbcdefghijklmnopqrstuvwXyz + End of testfile + /* + * HEre is a new .c file + */]]) + end) +end) diff --git a/test/functional/legacy/015_alignment_spec.lua b/test/functional/legacy/015_alignment_spec.lua index e71f9d1c90..3b19f4ff42 100644 --- a/test/functional/legacy/015_alignment_spec.lua +++ b/test/functional/legacy/015_alignment_spec.lua @@ -3,7 +3,7 @@ -- Also test undo after ":%s" and formatting. local helpers = require('test.functional.helpers') -local feed, insert, source = helpers.feed, helpers.insert, helpers.source +local feed, insert = helpers.feed, helpers.insert local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect describe('alignment', function() diff --git a/test/functional/legacy/022_line_ending_spec.lua b/test/functional/legacy/022_line_ending_spec.lua index 4b897a7c95..a841378a82 100644 --- a/test/functional/legacy/022_line_ending_spec.lua +++ b/test/functional/legacy/022_line_ending_spec.lua @@ -1,7 +1,7 @@ -- Tests for file with some lines ending in CTRL-M, some not local helpers = require('test.functional.helpers') -local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert +local clear, feed = helpers.clear, helpers.feed local execute, expect = helpers.execute, helpers.expect describe('line ending', function() diff --git a/test/functional/legacy/023_edit_arguments_spec.lua b/test/functional/legacy/023_edit_arguments_spec.lua index e68af9758d..15b30bfa3a 100644 --- a/test/functional/legacy/023_edit_arguments_spec.lua +++ b/test/functional/legacy/023_edit_arguments_spec.lua @@ -1,7 +1,7 @@ -- Tests for complicated + argument to :edit command local helpers = require('test.functional.helpers') -local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert +local clear, insert = helpers.clear, helpers.insert local execute, expect = helpers.execute, helpers.expect describe(':edit', function() diff --git a/test/functional/legacy/026_execute_while_if_spec.lua b/test/functional/legacy/026_execute_while_if_spec.lua index ffe37819de..f17bb79702 100644 --- a/test/functional/legacy/026_execute_while_if_spec.lua +++ b/test/functional/legacy/026_execute_while_if_spec.lua @@ -1,7 +1,7 @@ -- Test for :execute, :while and :if local helpers = require('test.functional.helpers') -local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert +local clear = helpers.clear local execute, expect = helpers.execute, helpers.expect local source = helpers.source diff --git a/test/functional/legacy/027_expand_file_names_spec.lua b/test/functional/legacy/027_expand_file_names_spec.lua index d31f29d38a..4778d16d43 100644 --- a/test/functional/legacy/027_expand_file_names_spec.lua +++ b/test/functional/legacy/027_expand_file_names_spec.lua @@ -1,10 +1,10 @@ -- Test for expanding file names local helpers = require('test.functional.helpers') -local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local execute, expect = helpers.execute, helpers.expect +local clear, feed = helpers.clear, helpers.feed +local execute = helpers.execute local curbuf_contents = helpers.curbuf_contents -local eq, eval = helpers.eq, helpers.eval +local eq = helpers.eq describe('expand file name', function() setup(clear) diff --git a/test/functional/legacy/029_join_spec.lua b/test/functional/legacy/029_join_spec.lua index eafa50d88c..25a072ad6e 100644 --- a/test/functional/legacy/029_join_spec.lua +++ b/test/functional/legacy/029_join_spec.lua @@ -1,7 +1,7 @@ -- Test for joining lines with marks in them (and with 'joinspaces' set/reset) local helpers = require('test.functional.helpers') -local feed, insert, source = helpers.feed, helpers.insert, helpers.source +local feed, insert = helpers.feed, helpers.insert local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect describe('joining lines', function() diff --git a/test/functional/legacy/031_close_commands_spec.lua b/test/functional/legacy/031_close_commands_spec.lua index 78e71b5fb1..b79b1903ba 100644 --- a/test/functional/legacy/031_close_commands_spec.lua +++ b/test/functional/legacy/031_close_commands_spec.lua @@ -84,6 +84,28 @@ describe('Commands that close windows and/or buffers', function() feed('GA 4<Esc>:all!<CR>') execute('1wincmd w') expect('testtext 2 2 2') + + -- Test ":q!" and hidden buffer. + execute('bw! Xtest1 Xtest2 Xtest3 Xtest4') + execute('sp Xtest1') + execute('wincmd w') + execute('bw!') + execute('set modified') + execute('bot sp Xtest2') + execute('set modified') + execute('bot sp Xtest3') + execute('set modified') + execute('wincmd t') + execute('hide') + execute('q!') + expect('testtext 3') + execute('q!') + feed('<CR>') + expect('testtext 1') + source([[ + q! + " Now nvim should have exited + throw "Oh, Not finished yet."]]) end) teardown(function() diff --git a/test/functional/legacy/035_increment_and_decrement_spec.lua b/test/functional/legacy/035_increment_and_decrement_spec.lua index 20c0cc4206..e6252c384b 100644 --- a/test/functional/legacy/035_increment_and_decrement_spec.lua +++ b/test/functional/legacy/035_increment_and_decrement_spec.lua @@ -11,34 +11,40 @@ describe('increment and decrement commands', function() it('should work', function() -- Insert some numbers in various bases. insert([[ - 100 0x100 077 0 - 100 0x100 077 + 0b101 100 0x100 077 0 + 0b101 100 0x100 077 100 0x100 077 0xfF 0xFf - 100 0x100 077]]) + 100 0x100 077 + 0x0b101 0b1101]]) -- Increment and decrement numbers in the first row, interpreting the -- numbers as decimal, octal or hexadecimal. - execute('set nrformats=octal,hex', '1') - feed('102ll64128$') + execute('set nrformats=bin,octal,hex', '1') + feed('63l102ll64128$') -- For the second row, treat the numbers as decimal or octal. -- 0x100 should be interpreted as decimal 0, the character x, and decimal 100. execute('set nrformats=octal', '2') - feed('0102l2w65129blx6lD') + feed('0w102l2w65129blx6lD') -- For the third row, treat the numbers as decimal or hexadecimal. -- 077 should be interpreted as decimal 77. execute('set nrformats=hex', '3') feed('0101l257Txldt ') - -- For the last row, interpret all numbers as decimal. + -- For the fourth row, interpret all numbers as decimal. execute('set nrformats=', '4') feed('0200l100w78') + -- For the last row, interpret as binary and hexadecimal. + execute('set nrformats=bin,hex', '5') + feed('010065l6432') + expect([[ - 0 0x0ff 0000 -1 - 0 1x100 0777777 + 0b011 0 0x0ff 0000 -1 + 1b101 0 1x100 0777777 -1 0x0 078 0xFE 0xfe - -100 -100x100 000]]) + -100 -100x100 000 + 0x0b0de 0b0101101]]) end) end) diff --git a/test/functional/legacy/036_regexp_character_classes_spec.lua b/test/functional/legacy/036_regexp_character_classes_spec.lua new file mode 100644 index 0000000000..de080f4b43 --- /dev/null +++ b/test/functional/legacy/036_regexp_character_classes_spec.lua @@ -0,0 +1,271 @@ +-- Test character classes in regexp using regexpengine 0, 1, 2. + +local helpers = require('test.functional.helpers') +local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect +local source, write_file = helpers.source, helpers.write_file +local os_name = helpers.os_name + +local function sixlines(text) + local result = '' + for _ = 1, 6 do + result = result .. text .. '\n' + end + return result +end + +local function diff(text, nodedent) + local tmpname = os.tmpname() + if os_name() == 'osx' and string.match(tmpname, '^/tmp') then + tmpname = '/private'..tmpname + end + execute('w! '..tmpname) + helpers.wait() + local data = io.open(tmpname):read('*all') + if nodedent then + helpers.eq(text, data) + else + helpers.eq(helpers.dedent(text), data) + end + os.remove(tmpname) +end + +describe('character classes in regexp', function() + local ctrl1 = '\t\012\r' + local punct1 = " !\"#$%&'()#+'-./" + local digits = '0123456789' + local punct2 = ':;<=>?@' + local upper = 'ABCDEFGHIXYZ' + local punct3 = '[\\]^_`' + local lower = 'abcdefghiwxyz' + local punct4 = '{|}~' + local ctrl2 = '\127\128\130\144\155' + local iso_text = '\166\177\188\199\211\233' -- "¦±¼ÇÓé" in utf-8 + setup(function() + -- The original test32.in file was not in utf-8 encoding and did also + -- contain some control characters. We use lua escape sequences to write + -- them to the test file. + local line = ctrl1..punct1..digits..punct2..upper..punct3..lower..punct4..ctrl2..iso_text + write_file('test36.in', sixlines(line)) + end) + before_each(function() + clear() + execute('e test36.in') + end) + teardown(function() + os.remove('test36.in') + end) + + it('is working', function() + source([[ + 1 s/\%#=0\d//g + 2 s/\%#=1\d//g + 3 s/\%#=2\d//g + 4 s/\%#=0[0-9]//g + 5 s/\%#=1[0-9]//g + 6 s/\%#=2[0-9]//g]]) + diff(sixlines(ctrl1..punct1..punct2..upper..punct3..lower..punct4.. + ctrl2..iso_text)) + end) + it('is working', function() + source([[ + 1 s/\%#=0\D//g + 2 s/\%#=1\D//g + 3 s/\%#=2\D//g + 4 s/\%#=0[^0-9]//g + 5 s/\%#=1[^0-9]//g + 6 s/\%#=2[^0-9]//g]]) + expect([[ + 0123456789 + 0123456789 + 0123456789 + 0123456789 + 0123456789 + 0123456789]]) + end) + it('is working', function() + source([[ + 1 s/\%#=0\o//g + 2 s/\%#=1\o//g + 3 s/\%#=2\o//g + 4 s/\%#=0[0-7]//g + 5 s/\%#=1[0-7]//g + 6 s/\%#=2[0-7]//g]]) + diff(sixlines(ctrl1..punct1..'89'..punct2..upper..punct3..lower..punct4..ctrl2.. + iso_text)) + end) + it('is working', function() + source([[ + 1 s/\%#=0\O//g + 2 s/\%#=1\O//g + 3 s/\%#=2\O//g + 4 s/\%#=0[^0-7]//g + 5 s/\%#=1[^0-7]//g + 6 s/\%#=2[^0-7]//g]]) + expect([[ + 01234567 + 01234567 + 01234567 + 01234567 + 01234567 + 01234567]]) + end) + it('is working', function() + source([[ + 1 s/\%#=0\x//g + 2 s/\%#=1\x//g + 3 s/\%#=2\x//g + 4 s/\%#=0[0-9A-Fa-f]//g + 5 s/\%#=1[0-9A-Fa-f]//g + 6 s/\%#=2[0-9A-Fa-f]//g]]) + diff(sixlines(ctrl1..punct1..punct2..'GHIXYZ'..punct3..'ghiwxyz'..punct4..ctrl2..iso_text)) + end) + it('is working', function() + source([[ + 1 s/\%#=0\X//g + 2 s/\%#=1\X//g + 3 s/\%#=2\X//g + 4 s/\%#=0[^0-9A-Fa-f]//g + 5 s/\%#=1[^0-9A-Fa-f]//g + 6 s/\%#=2[^0-9A-Fa-f]//g]]) + expect([[ + 0123456789ABCDEFabcdef + 0123456789ABCDEFabcdef + 0123456789ABCDEFabcdef + 0123456789ABCDEFabcdef + 0123456789ABCDEFabcdef + 0123456789ABCDEFabcdef]]) + end) + it('is working', function() + source([[ + 1 s/\%#=0\w//g + 2 s/\%#=1\w//g + 3 s/\%#=2\w//g + 4 s/\%#=0[0-9A-Za-z_]//g + 5 s/\%#=1[0-9A-Za-z_]//g + 6 s/\%#=2[0-9A-Za-z_]//g]]) + diff(sixlines(ctrl1..punct1..punct2..'[\\]^`'..punct4..ctrl2..iso_text)) + end) + it('is working', function() + source([[ + 1 s/\%#=0\W//g + 2 s/\%#=1\W//g + 3 s/\%#=2\W//g + 4 s/\%#=0[^0-9A-Za-z_]//g + 5 s/\%#=1[^0-9A-Za-z_]//g + 6 s/\%#=2[^0-9A-Za-z_]//g]]) + expect([[ + 0123456789ABCDEFGHIXYZ_abcdefghiwxyz + 0123456789ABCDEFGHIXYZ_abcdefghiwxyz + 0123456789ABCDEFGHIXYZ_abcdefghiwxyz + 0123456789ABCDEFGHIXYZ_abcdefghiwxyz + 0123456789ABCDEFGHIXYZ_abcdefghiwxyz + 0123456789ABCDEFGHIXYZ_abcdefghiwxyz]]) + end) + it('is working', function() + source([[ + 1 s/\%#=0\h//g + 2 s/\%#=1\h//g + 3 s/\%#=2\h//g + 4 s/\%#=0[A-Za-z_]//g + 5 s/\%#=1[A-Za-z_]//g + 6 s/\%#=2[A-Za-z_]//g]]) + diff(sixlines(ctrl1..punct1..digits..punct2..'[\\]^`'..punct4..ctrl2.. + iso_text)) + end) + it('is working', function() + source([[ + 1 s/\%#=0\H//g + 2 s/\%#=1\H//g + 3 s/\%#=2\H//g + 4 s/\%#=0[^A-Za-z_]//g + 5 s/\%#=1[^A-Za-z_]//g + 6 s/\%#=2[^A-Za-z_]//g]]) + expect([[ + ABCDEFGHIXYZ_abcdefghiwxyz + ABCDEFGHIXYZ_abcdefghiwxyz + ABCDEFGHIXYZ_abcdefghiwxyz + ABCDEFGHIXYZ_abcdefghiwxyz + ABCDEFGHIXYZ_abcdefghiwxyz + ABCDEFGHIXYZ_abcdefghiwxyz]]) + end) + it('is working', function() + source([[ + 1 s/\%#=0\a//g + 2 s/\%#=1\a//g + 3 s/\%#=2\a//g + 4 s/\%#=0[A-Za-z]//g + 5 s/\%#=1[A-Za-z]//g + 6 s/\%#=2[A-Za-z]//g]]) + diff(sixlines(ctrl1..punct1..digits..punct2..punct3..punct4..ctrl2..iso_text)) + end) + it('is working', function() + source([[ + 1 s/\%#=0\A//g + 2 s/\%#=1\A//g + 3 s/\%#=2\A//g + 4 s/\%#=0[^A-Za-z]//g + 5 s/\%#=1[^A-Za-z]//g + 6 s/\%#=2[^A-Za-z]//g]]) + expect([[ + ABCDEFGHIXYZabcdefghiwxyz + ABCDEFGHIXYZabcdefghiwxyz + ABCDEFGHIXYZabcdefghiwxyz + ABCDEFGHIXYZabcdefghiwxyz + ABCDEFGHIXYZabcdefghiwxyz + ABCDEFGHIXYZabcdefghiwxyz]]) + end) + it('is working', function() + source([[ + 1 s/\%#=0\l//g + 2 s/\%#=1\l//g + 3 s/\%#=2\l//g + 4 s/\%#=0[a-z]//g + 5 s/\%#=1[a-z]//g + 6 s/\%#=2[a-z]//g]]) + diff(sixlines(ctrl1..punct1..digits..punct2..upper..punct3..punct4.. + ctrl2..iso_text)) + end) + it('is working', function() + source([[ + 1 s/\%#=0\L//g + 2 s/\%#=1\L//g + 3 s/\%#=2\L//g + 4 s/\%#=0[^a-z]//g + 5 s/\%#=1[^a-z]//g + 6 s/\%#=2[^a-z]//g]]) + expect([[ + abcdefghiwxyz + abcdefghiwxyz + abcdefghiwxyz + abcdefghiwxyz + abcdefghiwxyz + abcdefghiwxyz]]) + end) + it('is working', function() + source([[ + 1 s/\%#=0\u//g + 2 s/\%#=1\u//g + 3 s/\%#=2\u//g + 4 s/\%#=0[A-Z]//g + 5 s/\%#=1[A-Z]//g + 6 s/\%#=2[A-Z]//g]]) + diff(sixlines(ctrl1..punct1..digits..punct2..punct3..lower..punct4.. + ctrl2..iso_text)) + end) + it('is working', function() + source([[ + 1 s/\%#=0\U//g + 2 s/\%#=1\U//g + 3 s/\%#=2\U//g + 4 s/\%#=0[^A-Z]//g + 5 s/\%#=1[^A-Z]//g + 6 s/\%#=2[^A-Z]//g]]) + expect([[ + ABCDEFGHIXYZ + ABCDEFGHIXYZ + ABCDEFGHIXYZ + ABCDEFGHIXYZ + ABCDEFGHIXYZ + ABCDEFGHIXYZ]]) + end) +end) diff --git a/test/functional/legacy/038_virtual_replace_spec.lua b/test/functional/legacy/038_virtual_replace_spec.lua index 94503bd42a..10d42f0cea 100644 --- a/test/functional/legacy/038_virtual_replace_spec.lua +++ b/test/functional/legacy/038_virtual_replace_spec.lua @@ -1,7 +1,7 @@ -- Test Virtual replace mode. local helpers = require('test.functional.helpers') -local feed, insert, source = helpers.feed, helpers.insert, helpers.source +local feed = helpers.feed local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect describe('Virtual replace mode', function() diff --git a/test/functional/legacy/039_visual_block_mode_commands_spec.lua b/test/functional/legacy/039_visual_block_mode_commands_spec.lua new file mode 100644 index 0000000000..7195d7d11d --- /dev/null +++ b/test/functional/legacy/039_visual_block_mode_commands_spec.lua @@ -0,0 +1,234 @@ +-- Test Visual block mode commands +-- And test "U" in Visual mode, also on German sharp S. + +local helpers = require('test.functional.helpers') +local nvim, eq = helpers.meths, helpers.eq +local insert, feed = helpers.insert, helpers.feed +local clear, expect = helpers.clear, helpers.expect +local execute = helpers.execute + +describe('Visual block mode', function() + + before_each(function() + clear() + + execute('set ts&vi sw&vi sts&vi noet') -- Vim compatible + end) + + it('should shift, insert, replace and change a block', function() + insert([[ + abcdefghijklm + abcdefghijklm + abcdefghijklm + abcdefghijklm + abcdefghijklm]]) + + feed('gg') + -- Test shift-right of a block + feed('jllll<C-v>jj>wll<C-v>jlll><CR>') + -- Test shift-left of a block + feed('G$hhhh<C-v>kk<lt>') + -- Test block-insert + feed('Gkl<C-v>kkkIxyz<ESC>') + -- Test block-replace + feed('Gllll<C-v>kkklllrq') + -- Test block-change + feed('G$khhh<C-v>hhkkcmno<ESC>') + + expect([[ + axyzbcdefghijklm + axyzqqqq mno ghijklm + axyzqqqqef mno ghijklm + axyzqqqqefgmnoklm + abcdqqqqijklm]]) + end) + + it('should insert a block using cursor keys for movement', function() + insert([[ + aaaaaa + bbbbbb + cccccc + dddddd + + xaaa + bbbb + cccc + dddd]]) + + execute('/^aa') + feed('l<C-v>jjjlllI<Right><Right> <ESC>') + execute('/xaaa$') + feed('<C-v>jjjI<lt>><Left>p<ESC>') + + expect([[ + aaa aaa + bbb bbb + ccc ccc + ddd ddd + + <p>xaaa + <p>bbbb + <p>cccc + <p>dddd]]) + end) + + it('should create a block', function() + insert([[ + A23 + 4567 + + B23 + 4567 + + C23 + 4567]]) + + -- Test for Visual block was created with the last <C-v>$. + execute('/^A23$/') + feed('l<C-v>j$Aab<ESC>') + -- Test for Visual block was created with the middle <C-v>$ (1). + execute('/^B23$/') + feed('l<C-v>j$hAab<ESC>') + -- Test for Visual block was created with the middle <C-v>$ (2). + execute('/^C23$/') + feed('l<C-v>j$hhAab<ESC>') + + expect([[ + A23ab + 4567ab + + B23 ab + 4567ab + + C23ab + 456ab7]]) + end) + + it('should insert and append a block when virtualedit=all', function() + insert([[ + line1 + line2 + line3 + ]]) + + -- Test for Visual block insert when virtualedit=all and utf-8 encoding. + execute('set ve=all') + execute('/\t\tline') + feed('07l<C-v>jjIx<ESC>') + + expect([[ + x line1 + x line2 + x line3 + ]]) + + -- Test for Visual block append when virtualedit=all. + feed('012l<C-v>jjAx<ESC>') + + expect([[ + x x line1 + x x line2 + x x line3 + ]]) + end) + + it('should make a selected part uppercase', function() + -- GUe must uppercase a whole word, also when ß changes to SS. + feed('Gothe youtußeuu end<ESC>Ypk0wgUe<CR>') + -- GUfx must uppercase until x, inclusive. + feed('O- youßtußexu -<ESC>0fogUfx<CR>') + -- VU must uppercase a whole line. + feed('YpkVU<CR>') + -- Same, when it's the last line in the buffer. + feed('YPGi111<ESC>VUddP<CR>') + -- Uppercase two lines. + feed('Oblah di<CR>') + feed('doh dut<ESC>VkUj<CR>') + -- Uppercase part of two lines. + feed('ddppi333<ESC>k0i222<esc>fyllvjfuUk<CR>') + + expect([[ + + the YOUTUSSEUU end + - yOUSSTUSSEXu - + THE YOUTUSSEUU END + 111THE YOUTUSSEUU END + BLAH DI + DOH DUT + 222the yoUTUSSEUU END + 333THE YOUTUßeuu end]]) + end) + + it('should replace using Enter or NL', function() + -- Visual replace using Enter or NL. + feed('G3o123456789<ESC>2k05l<C-v>2jr<CR>') + feed('G3o98765<ESC>2k02l<C-v>2jr<C-v><CR>') + feed('G3o123456789<ESC>2k05l<C-v>2jr<CR>') + feed('G3o98765<ESC>2k02l<C-v>2jr<C-v><Nul>') + + local expected = [[ + + 12345 + 789 + 12345 + 789 + 12345 + 789 + 98<CR>65 + 98<CR>65 + 98<CR>65 + 12345 + 789 + 12345 + 789 + 12345 + 789 + 98<Nul>65 + 98<Nul>65 + 98<Nul>65]] + expected = expected:gsub('<CR>', '\r') + expected = expected:gsub('<Nul>', '\000') + + expect(expected) + end) + + it('should treat cursor position correctly when virtualedit=block', function() + insert([[ + 12345 + 789 + 98765]]) + + -- Test cursor position. When virtualedit=block and Visual block mode and $gj. + execute('set ve=block') + feed('G2l') + feed('2k<C-v>$gj<ESC>') + execute([[let cpos=getpos("'>")]]) + local cpos = nvim.get_var('cpos') + local expected = { + col = 4, + off = 0 + } + local actual = { + col = cpos[3], + off = cpos[4] + } + + eq(expected, actual) + end) + + it('should replace spaces in front of the block with tabs', function() + insert([[ + #define BO_ALL 0x0001 + #define BO_BS 0x0002 + #define BO_CRSR 0x0004]]) + + -- Block_insert when replacing spaces in front of the block with tabs. + execute('set ts=8 sts=4 sw=4') + feed('ggf0<C-v>2jI<TAB><ESC>') + + expect([[ + #define BO_ALL 0x0001 + #define BO_BS 0x0002 + #define BO_CRSR 0x0004]]) + end) +end) diff --git a/test/functional/legacy/044_099_regexp_multibyte_magic_spec.lua b/test/functional/legacy/044_099_regexp_multibyte_magic_spec.lua index efe61aa354..2a4c0149fa 100644 --- a/test/functional/legacy/044_099_regexp_multibyte_magic_spec.lua +++ b/test/functional/legacy/044_099_regexp_multibyte_magic_spec.lua @@ -4,7 +4,7 @@ -- This test contains both "test44" and "test99" from the old test suite. local helpers = require('test.functional.helpers') -local feed, insert, source = helpers.feed, helpers.insert, helpers.source +local feed, insert = helpers.feed, helpers.insert local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect -- Runs the test protocol with the given 'regexpengine' setting. In the old test @@ -31,7 +31,8 @@ local function run_test_with_regexpengine(regexpengine) h AÀÁÂÃÄÅĀĂĄǍǞǠẢ BḂḆ CÇĆĈĊČ DĎĐḊḎḐ EÈÉÊËĒĔĖĘĚẺẼ FḞ GĜĞĠĢǤǦǴḠ HĤĦḢḦḨ IÌÍÎÏĨĪĬĮİǏỈ JĴ KĶǨḰḴ LĹĻĽĿŁḺ MḾṀ NÑŃŅŇṄṈ OÒÓÔÕÖØŌŎŐƠǑǪǬỎ PṔṖ Q RŔŖŘṘṞ SŚŜŞŠṠ TŢŤŦṪṮ UÙÚÛÜŨŪŬŮŰŲƯǓỦ VṼ WŴẀẂẄẆ XẊẌ YÝŶŸẎỲỶỸ ZŹŻŽƵẐẔ i aàáâãäåāăąǎǟǡả bḃḇ cçćĉċč dďđḋḏḑ eèéêëēĕėęěẻẽ fḟ gĝğġģǥǧǵḡ hĥħḣḧḩẖ iìíîïĩīĭįǐỉ jĵǰ kķǩḱḵ lĺļľŀłḻ mḿṁ nñńņňʼnṅṉ oòóôõöøōŏőơǒǫǭỏ pṕṗ q rŕŗřṙṟ sśŝşšṡ tţťŧṫṯẗ uùúûüũūŭůűųưǔủ vṽ wŵẁẃẅẇẘ xẋẍ yýÿŷẏẙỳỷỹ zźżžƶẑẕ j 0123❤x - k combinations]]) + k combinations + l ä ö ü ᾱ̆́]]) execute('set re=' .. regexpengine) @@ -85,6 +86,11 @@ local function run_test_with_regexpengine(regexpengine) execute([[let @w=':%s#comb[i]nations#œ̄ṣ́m̥̄ᾱ̆́#g']]) execute('@w') + -- Line l. Ex command ":s/ \?/ /g" should NOT split multi-byte characters + -- into bytes (fixed by vim-7.3.192). + execute([[/^l]]) + execute([[s/ \?/ /g]]) + -- Additional tests. Test matchstr() with multi-byte characters. feed('G') execute([[put =matchstr(\"אבגד\", \".\", 0, 2)]]) -- ב @@ -123,6 +129,7 @@ local function run_test_with_regexpengine(regexpengine) i aàáâãäåāăąǎǟǡả bḃḇ cçćĉċč dďđḋḏḑ eèéêëēĕėęěẻẽ fḟ gĝğġģǥǧǵḡ hĥħḣḧḩẖ iìíîïĩīĭįǐỉ jĵǰ kķǩḱḵ lĺļľŀłḻ mḿṁ nñńņňʼnṅṉ oòóôõöøōŏőơǒǫǭỏ pṕṗ q rŕŗřṙṟ sśŝşšṡ tţťŧṫṯẗ uùúûüũūŭůűųưǔủ vṽ wŵẁẃẅẇẘ xẋẍ yýÿŷẏẙỳỷỹ zźżžƶẑ j 012❤ k œ̄ṣ́m̥̄ᾱ̆́ + l ä ö ü ᾱ̆́ ב בג א diff --git a/test/functional/legacy/045_folding_spec.lua b/test/functional/legacy/045_folding_spec.lua new file mode 100644 index 0000000000..04b623ff3b --- /dev/null +++ b/test/functional/legacy/045_folding_spec.lua @@ -0,0 +1,139 @@ +-- Tests for folding. + +local helpers = require('test.functional.helpers') +local feed, insert, clear, execute, expect = + helpers.feed, helpers.insert, helpers.clear, helpers.execute, helpers.expect + +describe('folding', function() + before_each(clear) + + it('is working', function() + insert([[ + 1 aa + 2 bb + 3 cc + 4 dd {{{ + 5 ee {{{ }}} + 6 ff }}} + 7 gg + 8 hh + 9 ii + a jj + b kk + last]]) + + -- Basic test if a fold can be created, opened, moving to the end and + -- closed. + execute('/^1') + feed('zf2j') + execute('call append("$", "manual " . getline(foldclosed(".")))') + feed('zo') + execute('call append("$", foldclosed("."))') + feed(']z') + execute('call append("$", getline("."))') + feed('zc') + execute('call append("$", getline(foldclosed(".")))') + -- Test folding with markers. + execute('set fdm=marker fdl=1 fdc=3') + execute('/^5') + execute('call append("$", "marker " . foldlevel("."))') + feed('[z') + execute('call append("$", foldlevel("."))') + feed('jo{{ <esc>r{jj') + execute('call append("$", foldlevel("."))') + feed('kYpj') + execute('call append("$", foldlevel("."))') + -- Test folding with indent. + execute('set fdm=indent sw=2') + execute('/^2 b') + feed('i <esc>jI <esc>') + execute('call append("$", "indent " . foldlevel("."))') + feed('k') + execute('call append("$", foldlevel("."))') + -- Test syntax folding. + execute('set fdm=syntax fdl=0') + execute('syn region Hup start="dd" end="ii" fold contains=Fd1,Fd2,Fd3') + execute('syn region Fd1 start="ee" end="ff" fold contained') + execute('syn region Fd2 start="gg" end="hh" fold contained') + execute('syn region Fd3 start="commentstart" end="commentend" fold contained') + feed('Gzk') + execute('call append("$", "folding " . getline("."))') + feed('k') + execute('call append("$", getline("."))') + feed('jAcommentstart <esc>Acommentend<esc>') + execute('set fdl=1') + feed('3j') + execute('call append("$", getline("."))') + execute('set fdl=0') + feed('zO<C-L>j') + execute('call append("$", getline("."))') + -- Test expression folding. + execute('fun Flvl()') + execute(' let l = getline(v:lnum)') + execute(' if l =~ "bb$"') + execute(' return 2') + execute(' elseif l =~ "gg$"') + execute(' return "s1"') + execute(' elseif l =~ "ii$"') + execute(' return ">2"') + execute(' elseif l =~ "kk$"') + execute(' return "0"') + execute(' endif') + execute(' return "="') + execute('endfun') + execute('set fdm=expr fde=Flvl()') + execute('/bb$') + execute('call append("$", "expr " . foldlevel("."))') + execute('/hh$') + execute('call append("$", foldlevel("."))') + execute('/ii$') + execute('call append("$", foldlevel("."))') + execute('/kk$') + execute('call append("$", foldlevel("."))') + execute('0,/^last/delete') + execute('delfun Flvl') + + -- Assert buffer contents. + expect([[ + manual 1 aa + -1 + 3 cc + 1 aa + marker 2 + 1 + 1 + 0 + indent 2 + 1 + folding 9 ii + 3 cc + 7 gg + 8 hh + expr 2 + 1 + 2 + 0]]) + end) + + it('can open after :move', function() + insert([[ + Test fdm=indent and :move bug END + line2 + Test fdm=indent START + line3 + line4]]) + + execute('set noai nosta') + execute('set fdm=indent') + execute('1m1') + feed('2jzc') + execute('m0') + + expect([[ + Test fdm=indent START + line3 + line4 + Test fdm=indent and :move bug END + line2]]) + end) +end) diff --git a/test/functional/legacy/046_multi_line_regexps_spec.lua b/test/functional/legacy/046_multi_line_regexps_spec.lua index f8a6143b18..b17ab42fe3 100644 --- a/test/functional/legacy/046_multi_line_regexps_spec.lua +++ b/test/functional/legacy/046_multi_line_regexps_spec.lua @@ -3,7 +3,7 @@ local helpers = require('test.functional.helpers') local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local execute, expect = helpers.execute, helpers.expect +local expect = helpers.expect describe('multi-line regexp', function() setup(clear) diff --git a/test/functional/legacy/051_highlight_spec.lua b/test/functional/legacy/051_highlight_spec.lua index 620df97aac..94c42b73e5 100644 --- a/test/functional/legacy/051_highlight_spec.lua +++ b/test/functional/legacy/051_highlight_spec.lua @@ -3,7 +3,7 @@ local Screen = require('test.functional.ui.screen') local helpers = require('test.functional.helpers') -local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert +local clear, feed = helpers.clear, helpers.feed local execute, expect = helpers.execute, helpers.expect local wait = helpers.wait diff --git a/test/functional/legacy/056_script_local_function_spec.lua b/test/functional/legacy/056_script_local_function_spec.lua index 147391ceb1..dec88e8001 100644 --- a/test/functional/legacy/056_script_local_function_spec.lua +++ b/test/functional/legacy/056_script_local_function_spec.lua @@ -3,7 +3,7 @@ local helpers = require('test.functional.helpers') local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local execute, expect = helpers.execute, helpers.expect +local expect = helpers.expect describe('source function', function() setup(clear) diff --git a/test/functional/legacy/057_sort_spec.lua b/test/functional/legacy/057_sort_spec.lua index 585b391198..36062ded3a 100644 --- a/test/functional/legacy/057_sort_spec.lua +++ b/test/functional/legacy/057_sort_spec.lua @@ -600,4 +600,90 @@ describe(':sort', function() eq('Vim(sort):E474: Invalid argument', eval('tmpvar')) expect(text) end) + + it('binary', function() + insert([[ + 0b111000 + 0b101100 + 0b101001 + 0b101001 + 0b101000 + 0b000000 + 0b001000 + 0b010000 + 0b101000 + 0b100000 + 0b101010 + 0b100010 + 0b100100 + 0b100010]]) + execute([[sort b]]) + expect([[ + 0b000000 + 0b001000 + 0b010000 + 0b100000 + 0b100010 + 0b100010 + 0b100100 + 0b101000 + 0b101000 + 0b101001 + 0b101001 + 0b101010 + 0b101100 + 0b111000]]) + end) + + it('binary with leading characters', function() + insert([[ + 0b100010 + 0b010000 + 0b101001 + b0b101100 + 0b100010 + 0b100100 + a0b001000 + 0b101000 + 0b101000 + a0b101001 + ab0b100000 + 0b101010 + 0b000000 + b0b111000]]) + execute([[sort b]]) + expect([[ + 0b000000 + a0b001000 + 0b010000 + ab0b100000 + 0b100010 + 0b100010 + 0b100100 + 0b101000 + 0b101000 + 0b101001 + a0b101001 + 0b101010 + b0b101100 + b0b111000]]) + end) + + it('float', function() + insert([[ + 1.234 + 0.88 + 123.456 + 1.15e-6 + -1.1e3 + -1.01e3]]) + execute([[sort f]]) + expect([[ + -1.1e3 + -1.01e3 + 1.15e-6 + 0.88 + 1.234 + 123.456]]) + end) end) diff --git a/test/functional/legacy/059_utf8_spell_checking_spec.lua b/test/functional/legacy/059_utf8_spell_checking_spec.lua index 5794e875a0..63df387be3 100644 --- a/test/functional/legacy/059_utf8_spell_checking_spec.lua +++ b/test/functional/legacy/059_utf8_spell_checking_spec.lua @@ -31,8 +31,6 @@ describe("spell checking with 'encoding' set to utf-8", function() RAR ? BAD ! - #NOSPLITSUGS - PFX I N 1 PFX I 0 in . @@ -92,8 +90,6 @@ describe("spell checking with 'encoding' set to utf-8", function() RAR ? BAD ! - #NOSPLITSUGS - PFX I N 1 PFX I 0 in . @@ -300,6 +296,24 @@ describe("spell checking with 'encoding' set to utf-8", function() tail/123 middle/77,1 ]]) + write_latin1('Xtest8.aff', [[ + SET ISO8859-1 + + NOSPLITSUGS + ]]) + write_latin1('Xtest8.dic', [[ + 1234 + foo + bar + faabar + ]]) + write_latin1('Xtest9.aff', [[ + ]]) + write_latin1('Xtest9.dic', [[ + 1234 + foo + bar + ]]) write_latin1('Xtest-sal.aff', [[ SET ISO8859-1 TRY esianrtolcdugmphbyfvkwjkqxz-ëéèêïîäàâöüû'ESIANRTOLCDUGMPHBYFVKWJKQXZ @@ -314,8 +328,6 @@ describe("spell checking with 'encoding' set to utf-8", function() RAR ? BAD ! - #NOSPLITSUGS - PFX I N 1 PFX I 0 in . @@ -483,6 +495,10 @@ describe("spell checking with 'encoding' set to utf-8", function() os.remove('Xtest6.dic') os.remove('Xtest7.aff') os.remove('Xtest7.dic') + os.remove('Xtest8.aff') + os.remove('Xtest8.dic') + os.remove('Xtest9.aff') + os.remove('Xtest9.dic') end) -- Function to test .aff/.dic with list of good and bad words. This was a @@ -940,4 +956,46 @@ describe("spell checking with 'encoding' set to utf-8", function() leadprobar ['leadprebar', 'lead prebar', 'leadbar']]=]) end) + + it('part 8-8', function() + insert([[ + 8good: foo bar faabar + bad: foobar barfoo + badend + ]]) + -- NOSPLITSUGS + test_one(8, 8) + -- Assert buffer contents. + execute('1,/^test 8-8/-1d') + expect([=[ + test 8-8 + # file: Xtest.utf-8.spl + bar + faabar + foo + ------- + bad + ['bar', 'foo'] + foobar + ['faabar', 'foo bar', 'bar'] + barfoo + ['bar foo', 'bar', 'foo']]=]) + end) + + it('part 9-9', function() + insert([[ + 9good: 0b1011 0777 1234 0x01ff + badend + ]]) + -- NOSPLITSUGS + test_one(9, 9) + -- Assert buffer contents. + execute('1,/^test 9-9/-1d') + expect([=[ + test 9-9 + # file: Xtest.utf-8.spl + bar + foo + -------]=]) + end) end) diff --git a/test/functional/legacy/060_exists_and_has_functions_spec.lua b/test/functional/legacy/060_exists_and_has_functions_spec.lua index e9b79b490d..7f44b35a4e 100644 --- a/test/functional/legacy/060_exists_and_has_functions_spec.lua +++ b/test/functional/legacy/060_exists_and_has_functions_spec.lua @@ -1,8 +1,8 @@ -- Tests for the exists() and has() functions. local helpers = require('test.functional.helpers') -local feed, insert, source = helpers.feed, helpers.insert, helpers.source -local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect +local source = helpers.source +local clear, expect = helpers.clear, helpers.expect local write_file = helpers.write_file describe('exists() and has() functions', function() diff --git a/test/functional/legacy/061_undo_tree_spec.lua b/test/functional/legacy/061_undo_tree_spec.lua index 8cc2a371bb..6db37bf1ff 100644 --- a/test/functional/legacy/061_undo_tree_spec.lua +++ b/test/functional/legacy/061_undo_tree_spec.lua @@ -1,8 +1,8 @@ -- Tests for undo tree and :earlier and :later. local helpers = require('test.functional.helpers') -local feed, insert, source, eq, eval, clear, execute, expect, wait, write_file - = helpers.feed, helpers.insert, helpers.source, helpers.eq, helpers.eval, +local feed, source, eq, eval, clear, execute, expect, wait, write_file = + helpers.feed, helpers.source, helpers.eq, helpers.eval, helpers.clear, helpers.execute, helpers.expect, helpers.wait, helpers.write_file @@ -97,9 +97,8 @@ describe('undo tree:', function() -- Retry up to 3 times. pcall() is _not_ used for the final attempt, so -- that failure messages can bubble up. - local success, result = false, '' - for i = 1, 2 do - success, result = pcall(test_earlier_later) + for _ = 1, 2 do + local success = pcall(test_earlier_later) if success then return end @@ -192,7 +191,7 @@ describe('undo tree:', function() end) it('undo an expression-register', function() - local normal_commands = 'o1\x1ba2\x12=string(123)\n\x1b' + local normal_commands = 'o1\027a2\018=string(123)\n\027' write_file('Xtest.source', normal_commands) feed('oa<esc>') diff --git a/test/functional/legacy/062_tab_pages_spec.lua b/test/functional/legacy/062_tab_pages_spec.lua index 6bbb06f9a7..f1c8b8d58b 100644 --- a/test/functional/legacy/062_tab_pages_spec.lua +++ b/test/functional/legacy/062_tab_pages_spec.lua @@ -86,21 +86,35 @@ describe('tab pages', function() feed('1gt') eq(1, eval('tabpagenr()')) execute('tabmove 5') - eq(6, eval('tabpagenr()')) - execute('tabmove -2') + eq(5, eval('tabpagenr()')) + execute('.tabmove') + eq(5, eval('tabpagenr()')) + execute('tabmove -') eq(4, eval('tabpagenr()')) + execute('tabmove +') + eq(5, eval('tabpagenr()')) + execute('tabmove -2') + eq(3, eval('tabpagenr()')) execute('tabmove +4') - eq(8, eval('tabpagenr()')) + eq(7, eval('tabpagenr()')) execute('tabmove') eq(10, eval('tabpagenr()')) execute('tabmove -20') eq(1, eval('tabpagenr()')) execute('tabmove +20') eq(10, eval('tabpagenr()')) + execute('0tabmove') + eq(1, eval('tabpagenr()')) + execute('$tabmove') + eq(10, eval('tabpagenr()')) + execute('tabmove 0') + eq(1, eval('tabpagenr()')) + execute('tabmove $') + eq(10, eval('tabpagenr()')) execute('3tabmove') eq(4, eval('tabpagenr()')) execute('7tabmove 5') - eq(6, eval('tabpagenr()')) + eq(5, eval('tabpagenr()')) execute('let a="No error caught."') execute('try') execute('tabmove foo') diff --git a/test/functional/legacy/063_match_and_matchadd_spec.lua b/test/functional/legacy/063_match_and_matchadd_spec.lua index d819db7812..23b4f4551b 100644 --- a/test/functional/legacy/063_match_and_matchadd_spec.lua +++ b/test/functional/legacy/063_match_and_matchadd_spec.lua @@ -2,10 +2,9 @@ local helpers = require('test.functional.helpers') local Screen = require('test.functional.ui.screen') -local feed, insert, source = helpers.feed, helpers.insert, helpers.source -local eval, clear, execute, expect = helpers.eval, helpers.clear, helpers.execute -local expect, eq, neq = helpers.expect, helpers.eq, helpers.neq -local command = helpers.command +local feed, insert = helpers.feed, helpers.insert +local eval, clear, execute = helpers.eval, helpers.clear, helpers.execute +local eq, neq = helpers.eq, helpers.neq describe('063: Test for ":match", "matchadd()" and related functions', function() setup(clear) @@ -86,11 +85,15 @@ describe('063: Test for ":match", "matchadd()" and related functions', function( execute("2match MyGroup2 /HUMPPA/") execute("3match MyGroup3 /VIM/") execute("let ml = getmatches()") - ml = eval("ml") + local ml = eval("ml") execute("call clearmatches()") execute("call setmatches(ml)") eq(ml, eval('getmatches()')) + + -- Check that "setmatches()" can correctly restore the matches from matchaddpos() execute("call clearmatches()") + execute("call setmatches(ml)") + eq(ml, eval('getmatches()')) -- Check that "setmatches()" will not add two matches with the same ID. The -- expected behaviour (for now) is to add the first match but not the diff --git a/test/functional/legacy/065_float_and_logic_operators_spec.lua b/test/functional/legacy/065_float_and_logic_operators_spec.lua index 5cdb0b7f58..e78b230956 100644 --- a/test/functional/legacy/065_float_and_logic_operators_spec.lua +++ b/test/functional/legacy/065_float_and_logic_operators_spec.lua @@ -1,7 +1,7 @@ -- Test for floating point and logical operators. local helpers = require('test.functional.helpers') -local feed, insert, source = helpers.feed, helpers.insert, helpers.source +local insert, source = helpers.insert, helpers.source local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect describe('floating point and logical operators', function() diff --git a/test/functional/legacy/067_augroup_exists_spec.lua b/test/functional/legacy/067_augroup_exists_spec.lua index 6d89ad6d55..dc4c9c7eeb 100644 --- a/test/functional/legacy/067_augroup_exists_spec.lua +++ b/test/functional/legacy/067_augroup_exists_spec.lua @@ -2,7 +2,7 @@ -- autocommands. local helpers = require('test.functional.helpers') -local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert +local clear = helpers.clear local execute, expect = helpers.execute, helpers.expect describe('augroup when calling exists()', function() diff --git a/test/functional/legacy/068_text_formatting_spec.lua b/test/functional/legacy/068_text_formatting_spec.lua new file mode 100644 index 0000000000..cac8be77f3 --- /dev/null +++ b/test/functional/legacy/068_text_formatting_spec.lua @@ -0,0 +1,207 @@ +local helpers = require('test.functional.helpers') +local feed, insert = helpers.feed, helpers.insert +local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect + +describe('text formatting', function() + setup(clear) + + it('is working', function() + -- The control character <C-A> (byte \x01) needs to be put in the buffer + -- directly. But the insert function sends the text to nvim in insert + -- mode so it has to be escaped with <C-V>. + insert([[ + Results of test68: + + + { + + + } + + + { + a b + + a + } + + + { + a + } + + + { + a b + #a b + } + + + { + 1 a + # 1 a + } + + + { + + x a + b + c + + } + + + { + # 1 a b + } + + + { + # x + # a b + } + + + { + 1aa + 2bb + } + + + /* abc def ghi jkl + * mno pqr stu + */ + + + # 1 xxxxx + ]]) + + execute('/^{/+1') + execute('set noai tw=2 fo=t') + feed('gRa b<esc>') + + execute('/^{/+1') + execute('set ai tw=2 fo=tw') + feed('gqgqjjllab<esc>') + + execute('/^{/+1') + execute('set tw=3 fo=t') + feed('gqgqo<cr>') + feed('a <C-V><C-A><esc><esc>') + + execute('/^{/+1') + execute('set tw=2 fo=tcq1 comments=:#') + feed('gqgqjgqgqo<cr>') + feed('a b<cr>') + feed('#a b<esc>') + + execute('/^{/+1') + execute('set tw=5 fo=tcn comments=:#') + feed('A b<esc>jA b<esc>') + + execute('/^{/+3') + execute('set tw=5 fo=t2a si') + feed('i <esc>A_<esc>') + + execute('/^{/+1') + execute('set tw=5 fo=qn comments=:#') + feed('gwap<cr>') + + execute('/^{/+1') + execute('set tw=5 fo=q2 comments=:#') + feed('gwap<cr>') + + execute('/^{/+2') + execute('set tw& fo=a') + feed('I^^<esc><esc>') + + execute('/mno pqr/') + execute('setl tw=20 fo=an12wcq comments=s1:/*,mb:*,ex:*/') + feed('A vwx yz<esc>') + + execute('/^#/') + execute('setl tw=12 fo=tqnc comments=:#') + feed('A foobar<esc>') + + -- Assert buffer contents. + expect([[ + Results of test68: + + + { + a + b + } + + + { + a + b + + a + b + } + + + { + a + + + a + + } + + + { + a b + #a b + + a b + #a b + } + + + { + 1 a + b + # 1 a + # b + } + + + { + + x a + b_ + c + + } + + + { + # 1 a + # b + } + + + { + # x a + # b + } + + + { 1aa ^^2bb } + + + /* abc def ghi jkl + * mno pqr stu + * vwx yz + */ + + + # 1 xxxxx + # foobar + ]]) + end) +end) diff --git a/test/functional/legacy/072_undo_file_spec.lua b/test/functional/legacy/072_undo_file_spec.lua index ce9129ff43..efcc2f2cc3 100644 --- a/test/functional/legacy/072_undo_file_spec.lua +++ b/test/functional/legacy/072_undo_file_spec.lua @@ -3,7 +3,7 @@ -- undo-able pieces. Do that by setting 'undolevels'. local helpers = require('test.functional.helpers') -local feed, insert, source = helpers.feed, helpers.insert, helpers.source +local feed, insert = helpers.feed, helpers.insert local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect describe('72', function() diff --git a/test/functional/legacy/074_global_var_in_viminfo_spec.lua b/test/functional/legacy/074_global_var_in_viminfo_spec.lua index 2428b7f74d..49c4827613 100644 --- a/test/functional/legacy/074_global_var_in_viminfo_spec.lua +++ b/test/functional/legacy/074_global_var_in_viminfo_spec.lua @@ -13,7 +13,7 @@ describe('storing global variables in ShaDa files', function() end) it('is working', function() - local nvim2 = helpers.spawn({helpers.nvim_prog, '-u', 'NONE', + local nvim2 = spawn({helpers.nvim_prog, '-u', 'NONE', '-i', 'Xviminfo', '--embed'}) helpers.set_session(nvim2) diff --git a/test/functional/legacy/075_maparg_spec.lua b/test/functional/legacy/075_maparg_spec.lua index 418abb14d4..82965f5cb2 100644 --- a/test/functional/legacy/075_maparg_spec.lua +++ b/test/functional/legacy/075_maparg_spec.lua @@ -2,7 +2,7 @@ -- Also test utf8 map with a 0x80 byte. local helpers = require('test.functional.helpers') -local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert +local clear, feed = helpers.clear, helpers.feed local execute, expect = helpers.execute, helpers.expect describe('maparg()', function() diff --git a/test/functional/legacy/077_mf_hash_grow_spec.lua b/test/functional/legacy/077_mf_hash_grow_spec.lua index 825f08e968..029fe98fe9 100644 --- a/test/functional/legacy/077_mf_hash_grow_spec.lua +++ b/test/functional/legacy/077_mf_hash_grow_spec.lua @@ -7,7 +7,7 @@ -- If it isn't available then the test will be skipped. local helpers = require('test.functional.helpers') -local feed, insert, source = helpers.feed, helpers.insert, helpers.source +local feed = helpers.feed local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect describe('mf_hash_grow()', function() diff --git a/test/functional/legacy/078_swapfile_recover_spec.lua b/test/functional/legacy/078_swapfile_recover_spec.lua index 33115c1317..e48fddaac1 100644 --- a/test/functional/legacy/078_swapfile_recover_spec.lua +++ b/test/functional/legacy/078_swapfile_recover_spec.lua @@ -4,9 +4,7 @@ -- pointer blocks. local helpers = require('test.functional.helpers') -local feed, insert, source = helpers.feed, helpers.insert, helpers.source -local clear, execute, expect, source = helpers.clear, helpers.execute, helpers.expect, helpers.source -local eval = helpers.eval +local clear, expect, source = helpers.clear, helpers.expect, helpers.source describe('78', function() setup(clear) diff --git a/test/functional/legacy/080_substitute_spec.lua b/test/functional/legacy/080_substitute_spec.lua index 89ef7a713c..96082364e0 100644 --- a/test/functional/legacy/080_substitute_spec.lua +++ b/test/functional/legacy/080_substitute_spec.lua @@ -3,7 +3,7 @@ -- Test for *:s%* on :substitute. local helpers = require('test.functional.helpers') -local feed, insert, source = helpers.feed, helpers.insert, helpers.source +local feed, insert = helpers.feed, helpers.insert local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect local eq, eval = helpers.eq, helpers.eval diff --git a/test/functional/legacy/082_string_comparison_spec.lua b/test/functional/legacy/082_string_comparison_spec.lua index 1615828ca0..933c6c8fa3 100644 --- a/test/functional/legacy/082_string_comparison_spec.lua +++ b/test/functional/legacy/082_string_comparison_spec.lua @@ -2,7 +2,7 @@ -- Also test "g~ap". local helpers = require('test.functional.helpers') -local feed, insert, source = helpers.feed, helpers.insert, helpers.source +local feed, source = helpers.feed, helpers.source local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect describe('case-insensitive string comparison in UTF-8', function() diff --git a/test/functional/legacy/083_tag_search_with_file_encoding_spec.lua b/test/functional/legacy/083_tag_search_with_file_encoding_spec.lua new file mode 100644 index 0000000000..6b5ee60568 --- /dev/null +++ b/test/functional/legacy/083_tag_search_with_file_encoding_spec.lua @@ -0,0 +1,110 @@ +-- Tests for tag search with !_TAG_FILE_ENCODING. + +local helpers = require('test.functional.helpers') +local insert, source, clear, expect, write_file = helpers.insert, + helpers.source, helpers.clear, helpers.expect, helpers.write_file + +local function has_iconv() + clear() -- ensures session + return 1 == helpers.eval('has("iconv")') +end + +describe('tag search with !_TAG_FILE_ENCODING', function() + setup(function() + clear() + -- Create some temp files that are needed for the test run. In the old + -- test suite this was done by putting the text inside the file test83.in + -- and executing some "/first/,/last/w! tmpfile" commands. + write_file('Xtags1.txt', 'text for tags1\nabcdefghijklmnopqrs\n') + write_file('Xtags2.txt', 'text for tags2\nABC\n') + write_file('Xtags3.txt', 'text for tags3\nABC\n') + write_file('Xtags1', [[ + !_TAG_FILE_ENCODING utf-8 // + abcdefghijklmnopqrs Xtags1.txt /abcdefghijklmnopqrs + ]]) + write_file('test83-tags2', + '!_TAG_FILE_ENCODING cp932 //\n' .. + '\130`\130a\130b Xtags2.txt /\130`\130a\130b\n' + ) + -- The last file is very long but repetetive and can be generated on the + -- fly. + local text = helpers.dedent([[ + !_TAG_FILE_SORTED 1 // + !_TAG_FILE_ENCODING cp932 // + ]]) + local line = ' Xtags3.txt /\130`\130a\130b\n' + for i = 1, 100 do + text = text .. 'abc' .. i .. line + end + write_file('test83-tags3', text) + end) + teardown(function() + os.remove('Xtags1') + os.remove('Xtags1.txt') + os.remove('Xtags2.txt') + os.remove('Xtags3.txt') + os.remove('test83-tags2') + os.remove('test83-tags3') + end) + + if not has_iconv() then + pending('skipped (missing iconv)', function() end) + else + it('is working', function() + + insert('Results of test83') + + -- Case1: + source([[ + new + set tags=Xtags1 + let v:errmsg = '' + tag abcdefghijklmnopqrs + if v:errmsg =~ 'E426:' || getline('.') != 'abcdefghijklmnopqrs' + close + put ='case1: failed' + else + close + put ='case1: ok' + endif + ]]) + + -- Case2: + source([[ + new + set tags=test83-tags2 + let v:errmsg = '' + tag /.BC + if v:errmsg =~ 'E426:' || getline('.') != 'ABC' + close + put ='case2: failed' + else + close + put ='case2: ok' + endif + ]]) + + -- Case3: + source([[ + new + set tags=test83-tags3 + let v:errmsg = '' + tag abc50 + if v:errmsg =~ 'E426:' || getline('.') != 'ABC' + close + put ='case3: failed' + else + close + put ='case3: ok' + endif + ]]) + + -- Assert buffer contents. + expect([[ + Results of test83 + case1: ok + case2: ok + case3: ok]]) + end) + end +end) diff --git a/test/functional/legacy/084_curswant_spec.lua b/test/functional/legacy/084_curswant_spec.lua index 55df5d3e73..946dd5e501 100644 --- a/test/functional/legacy/084_curswant_spec.lua +++ b/test/functional/legacy/084_curswant_spec.lua @@ -1,8 +1,8 @@ -- Tests for curswant not changing when setting an option. local helpers = require('test.functional.helpers') -local feed, insert, source = helpers.feed, helpers.insert, helpers.source -local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect +local insert, source = helpers.insert, helpers.source +local clear, expect = helpers.clear, helpers.expect describe('curswant', function() setup(clear) diff --git a/test/functional/legacy/088_conceal_tabs_spec.lua b/test/functional/legacy/088_conceal_tabs_spec.lua new file mode 100644 index 0000000000..c78f4e5c3e --- /dev/null +++ b/test/functional/legacy/088_conceal_tabs_spec.lua @@ -0,0 +1,96 @@ +-- Tests for correct display (cursor column position) with +conceal and +-- tabulators. + +local helpers = require('test.functional.helpers') +local feed, insert, clear, execute = + helpers.feed, helpers.insert, helpers.clear, helpers.execute + +local expect_pos = function(row, col) + return helpers.eq({row, col}, helpers.eval('[screenrow(), screencol()]')) +end + +describe('cursor and column position with conceal and tabulators', function() + setup(clear) + + it('are working', function() + insert([[ + start: + .concealed. text + |concealed| text + + .concealed. text + |concealed| text + + .a. .b. .c. .d. + |a| |b| |c| |d|]]) + + -- Conceal settings. + execute('set conceallevel=2') + execute('set concealcursor=nc') + execute('syntax match test /|/ conceal') + -- Start test. + execute('/^start:') + feed('ztj') + expect_pos(2, 1) + -- We should end up in the same column when running these commands on the + -- two lines. + feed('ft') + expect_pos(2, 17) + feed('$') + expect_pos(2, 20) + feed('0j') + expect_pos(3, 1) + feed('ft') + expect_pos(3, 17) + feed('$') + expect_pos(3, 20) + feed('j0j') + expect_pos(5, 8) + -- Same for next test block. + feed('ft') + expect_pos(5, 25) + feed('$') + expect_pos(5, 28) + feed('0j') + expect_pos(6, 8) + feed('ft') + expect_pos(6, 25) + feed('$') + expect_pos(6, 28) + feed('0j0j') + expect_pos(8, 1) + -- And check W with multiple tabs and conceals in a line. + feed('W') + expect_pos(8, 9) + feed('W') + expect_pos(8, 17) + feed('W') + expect_pos(8, 25) + feed('$') + expect_pos(8, 27) + feed('0j') + expect_pos(9, 1) + feed('W') + expect_pos(9, 9) + feed('W') + expect_pos(9, 17) + feed('W') + expect_pos(9, 25) + feed('$') + expect_pos(9, 26) + execute('set lbr') + feed('$') + expect_pos(9, 26) + execute('set list listchars=tab:>-') + feed('0') + expect_pos(9, 1) + feed('W') + expect_pos(9, 9) + feed('W') + expect_pos(9, 17) + feed('W') + expect_pos(9, 25) + feed('$') + expect_pos(9, 26) + end) +end) diff --git a/test/functional/legacy/089_number_relnumber_findfile_spec.lua b/test/functional/legacy/089_number_relnumber_findfile_spec.lua index 1f8e49cc81..f72ebf3f72 100644 --- a/test/functional/legacy/089_number_relnumber_findfile_spec.lua +++ b/test/functional/legacy/089_number_relnumber_findfile_spec.lua @@ -4,7 +4,7 @@ local helpers = require('test.functional.helpers') local feed = helpers.feed -local clear, execute, expect, source = helpers.clear, helpers.execute, helpers.expect, helpers.source +local clear, expect, source = helpers.clear, helpers.expect, helpers.source describe("setting 'number' and 'relativenumber'", function() setup(clear) diff --git a/test/functional/legacy/090_sha256_spec.lua b/test/functional/legacy/090_sha256_spec.lua index 35fbd5752e..95e50063a1 100644 --- a/test/functional/legacy/090_sha256_spec.lua +++ b/test/functional/legacy/090_sha256_spec.lua @@ -1,8 +1,8 @@ -- Tests for sha256() function. local helpers = require('test.functional.helpers') -local feed, insert, source = helpers.feed, helpers.insert, helpers.source -local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect +local insert, source = helpers.insert, helpers.source +local clear, expect = helpers.clear, helpers.expect describe('sha256()', function() setup(clear) diff --git a/test/functional/legacy/091_context_variables_spec.lua b/test/functional/legacy/091_context_variables_spec.lua index bb9c32b84f..2c46ef643c 100644 --- a/test/functional/legacy/091_context_variables_spec.lua +++ b/test/functional/legacy/091_context_variables_spec.lua @@ -1,8 +1,8 @@ -- Tests for getbufvar(), getwinvar(), gettabvar() and gettabwinvar(). local helpers = require('test.functional.helpers') -local feed, insert, source = helpers.feed, helpers.insert, helpers.source -local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect +local insert, source = helpers.insert, helpers.source +local clear, expect = helpers.clear, helpers.expect describe('context variables', function() setup(clear) @@ -13,6 +13,9 @@ describe('context variables', function() -- Test for getbufvar(). -- Use strings to test for memory leaks. source([[ + let t:testvar='abcd' + $put =string(gettabvar(1, 'testvar')) + $put =string(gettabvar(1, 'testvar')) let b:var_num = '1234' let def_num = '5678' $put =string(getbufvar(1, 'var_num')) @@ -125,6 +128,8 @@ describe('context variables', function() -- Assert buffer contents. expect([[ start: + 'abcd' + 'abcd' '1234' '1234' {'var_num': '1234'} diff --git a/test/functional/legacy/092_mksession_cursor_cols_utf8_spec.lua b/test/functional/legacy/092_mksession_cursor_cols_utf8_spec.lua index e0cc39dc40..f76ba25d7a 100644 --- a/test/functional/legacy/092_mksession_cursor_cols_utf8_spec.lua +++ b/test/functional/legacy/092_mksession_cursor_cols_utf8_spec.lua @@ -4,7 +4,7 @@ -- Same as legacy test 93 but using UTF-8 file encoding. local helpers = require('test.functional.helpers') -local feed, insert, source = helpers.feed, helpers.insert, helpers.source +local feed, insert = helpers.feed, helpers.insert local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect describe('store cursor position in session file in UTF-8', function() diff --git a/test/functional/legacy/093_mksession_cursor_cols_latin1_spec.lua b/test/functional/legacy/093_mksession_cursor_cols_latin1_spec.lua index 659e716721..bf3af1a827 100644 --- a/test/functional/legacy/093_mksession_cursor_cols_latin1_spec.lua +++ b/test/functional/legacy/093_mksession_cursor_cols_latin1_spec.lua @@ -4,7 +4,7 @@ -- Same as legacy test 92 but using Latin-1 file encoding. local helpers = require('test.functional.helpers') -local feed, insert, source = helpers.feed, helpers.insert, helpers.source +local feed, insert = helpers.feed, helpers.insert local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect describe('store cursor position in session file in Latin-1', function() diff --git a/test/functional/legacy/094_visual_mode_operators_spec.lua b/test/functional/legacy/094_visual_mode_operators_spec.lua index c4aebe4ecc..4dce39b8d2 100644 --- a/test/functional/legacy/094_visual_mode_operators_spec.lua +++ b/test/functional/legacy/094_visual_mode_operators_spec.lua @@ -24,6 +24,27 @@ local function source_user_functions() ]]) end +local function put_abc() + source([[ + $put ='a' + $put ='b' + $put ='c']]) +end + +local function put_aaabbbccc() + source([[ + $put ='aaa' + $put ='bbb' + $put ='ccc']]) +end + +local function define_select_mode_maps() + source([[ + snoremap <lt>End> <End> + snoremap <lt>Down> <Down> + snoremap <lt>Del> <Del>]]) +end + describe('Visual mode and operator', function() before_each(function() clear() @@ -150,4 +171,228 @@ describe('Visual mode and operator', function() ok ok]]) end) + + describe('characterwise visual mode:', function() + it('replace last line', function() + source([[ + $put ='a' + let @" = 'x']]) + feed('v$p') + + expect([[ + + x]]) + end) + + it('delete middle line', function() + put_abc() + feed('kkv$d') + + expect([[ + + b + c]]) + end) + + it('delete middle two line', function() + put_abc() + feed('kkvj$d') + + expect([[ + + c]]) + end) + + it('delete last line', function() + put_abc() + feed('v$d') + + expect([[ + + a + b + ]]) + end) + + it('delete last two line', function() + put_abc() + feed('kvj$d') + + expect([[ + + a + ]]) + end) + end) + + describe('characterwise select mode:', function() + before_each(function() + define_select_mode_maps() + end) + + it('delete middle line', function() + put_abc() + feed('kkgh<End><Del>') + + expect([[ + + b + c]]) + end) + + it('delete middle two line', function() + put_abc() + feed('kkgh<Down><End><Del>') + + expect([[ + + c]]) + end) + + it('delete last line', function() + put_abc() + feed('gh<End><Del>') + + expect([[ + + a + b + ]]) + end) + + it('delete last two line', function() + put_abc() + feed('kgh<Down><End><Del>') + + expect([[ + + a + ]]) + end) + end) + + describe('linewise select mode:', function() + before_each(function() + define_select_mode_maps() + end) + + it('delete middle line', function() + put_abc() + feed(' kkgH<Del> ') + + expect([[ + + b + c]]) + end) + + it('delete middle two line', function() + put_abc() + feed('kkgH<Down><Del>') + + expect([[ + + c]]) + end) + + it('delete last line', function() + put_abc() + feed('gH<Del>') + + expect([[ + + a + b]]) + end) + + it('delete last two line', function() + put_abc() + feed('kgH<Down><Del>') + + expect([[ + + a]]) + end) + end) + + describe('v_p:', function() + it('replace last character with line register at middle line', function() + put_aaabbbccc() + execute('-2yank') + feed('k$vp') + + expect([[ + + aaa + bb + aaa + + ccc]]) + end) + + it('replace last character with line register at middle line selecting newline', function() + put_aaabbbccc() + execute('-2yank') + feed('k$v$p') + + expect([[ + + aaa + bb + aaa + ccc]]) + end) + + it('replace last character with line register at last line', function() + put_aaabbbccc() + execute('-2yank') + feed('$vp') + + expect([[ + + aaa + bbb + cc + aaa + ]]) + end) + + it('replace last character with line register at last line selecting newline', function() + put_aaabbbccc() + execute('-2yank') + feed('$v$p') + + expect([[ + + aaa + bbb + cc + aaa + ]]) + end) + end) + + it('gv in exclusive select mode after operation', function() + source([[ + $put ='zzz ' + $put ='äà ' + set selection=exclusive]]) + feed('kv3lyjv3lpgvcxxx<Esc>') + + expect([[ + + zzz + xxx ]]) + end) + + it('gv in exclusive select mode without operation', function() + source([[ + $put ='zzz ' + set selection=exclusive]]) + feed('0v3l<Esc>gvcxxx<Esc>') + + expect([[ + + xxx ]]) + end) end) diff --git a/test/functional/legacy/095_regexp_multibyte_spec.lua b/test/functional/legacy/095_regexp_multibyte_spec.lua index 559222e2ff..a80a247612 100644 --- a/test/functional/legacy/095_regexp_multibyte_spec.lua +++ b/test/functional/legacy/095_regexp_multibyte_spec.lua @@ -4,8 +4,8 @@ -- actually tried. local helpers = require('test.functional.helpers') -local feed, insert, source = helpers.feed, helpers.insert, helpers.source -local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect +local insert, source = helpers.insert, helpers.source +local clear, expect = helpers.clear, helpers.expect describe('regex with multi-byte', function() setup(clear) diff --git a/test/functional/legacy/096_location_list_spec.lua b/test/functional/legacy/096_location_list_spec.lua index 2ccfd3530d..6e2f22ea33 100644 --- a/test/functional/legacy/096_location_list_spec.lua +++ b/test/functional/legacy/096_location_list_spec.lua @@ -7,7 +7,7 @@ -- it belongs to. local helpers = require('test.functional.helpers') -local feed, insert, source = helpers.feed, helpers.insert, helpers.source +local source = helpers.source local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect describe('location list', function() diff --git a/test/functional/legacy/097_glob_path_spec.lua b/test/functional/legacy/097_glob_path_spec.lua index 84f26478ac..5c467fbb20 100644 --- a/test/functional/legacy/097_glob_path_spec.lua +++ b/test/functional/legacy/097_glob_path_spec.lua @@ -3,7 +3,7 @@ -- characters. local helpers = require('test.functional.helpers') -local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert +local clear = helpers.clear local execute, expect = helpers.execute, helpers.expect describe('glob() and globpath()', function() diff --git a/test/functional/legacy/098_scrollbind_spec.lua b/test/functional/legacy/098_scrollbind_spec.lua index 7b2059e38b..6850e373ab 100644 --- a/test/functional/legacy/098_scrollbind_spec.lua +++ b/test/functional/legacy/098_scrollbind_spec.lua @@ -1,8 +1,8 @@ -- Test for 'scrollbind' causing an unexpected scroll of one of the windows. local helpers = require('test.functional.helpers') -local feed, insert, source = helpers.feed, helpers.insert, helpers.source -local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect +local source = helpers.source +local clear, expect = helpers.clear, helpers.expect describe('scrollbind', function() setup(clear) diff --git a/test/functional/legacy/100_undo_level_spec.lua b/test/functional/legacy/100_undo_level_spec.lua deleted file mode 100644 index 9143d9e540..0000000000 --- a/test/functional/legacy/100_undo_level_spec.lua +++ /dev/null @@ -1,142 +0,0 @@ --- Tests for 'undolevel' setting being global-local - -local helpers = require('test.functional.helpers') -local feed, insert, source = helpers.feed, helpers.insert, helpers.source -local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect - -describe('undolevel', function() - setup(clear) - - it('is working', function() - source([[ - set ul=5 - fu! FillBuffer() - for i in range(1,13) - put=i - exe "setg ul=" . &g:ul - endfor - endfu - fu! UndoLevel() - redir @z - setglobal undolevels? - echon ' global' - setlocal undolevels? - echon ' local' - redir end - $put z - endfu - - 0put ='ONE: expecting global undolevels: 5, local undolevels: -123456 (default)' - call FillBuffer() - setlocal undolevels< - earlier 10 - call UndoLevel() - set ff=unix - %yank A - %delete - - 0put ='TWO: expecting global undolevels: 5, local undolevels: 2 (first) then 10 (afterwards)' - setlocal ul=2 - call FillBuffer() - earlier 10 - call UndoLevel() - setlocal ul=10 - call UndoLevel() - set ff=unix - %yank A - %delete - setlocal undolevels< - redir @A - echo "global value shouldn't be changed and still be 5!" - echo 'ONE: expecting global undolevels: 5, local undolevels: -123456 (default)' - setglobal undolevels? - echon ' global' - setlocal undolevels? - echon ' local' - echo "" - redir end - - setglobal ul=50 - 1put ='global value should be changed to 50' - 2put ='THREE: expecting global undolevels: 50, local undolevels: -123456 (default)' - call UndoLevel() - set ff=unix - %yank A - %delete - setglobal lispwords=foo,bar,baz - setlocal lispwords-=foo - setlocal lispwords+=quux - redir @A - echo "Testing 'lispwords' local value" - setglobal lispwords? - setlocal lispwords? - echo &lispwords - echo '' - redir end - setlocal lispwords< - redir @A - echo "Testing 'lispwords' value reset" - setglobal lispwords? - setlocal lispwords? - echo &lispwords - redir end - - 0put a - $d - ]]) - - -- Assert buffer contents. - expect([[ - ONE: expecting global undolevels: 5, local undolevels: -123456 (default) - 1 - 2 - 3 - 4 - 5 - 6 - 7 - - - undolevels=5 global - undolevels=-123456 local - TWO: expecting global undolevels: 5, local undolevels: 2 (first) then 10 (afterwards) - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - - - undolevels=5 global - undolevels=2 local - - undolevels=5 global - undolevels=10 local - - global value shouldn't be changed and still be 5! - ONE: expecting global undolevels: 5, local undolevels: -123456 (default) - undolevels=5 global - undolevels=-123456 local - - global value should be changed to 50 - THREE: expecting global undolevels: 50, local undolevels: -123456 (default) - - undolevels=50 global - undolevels=-123456 local - - Testing 'lispwords' local value - lispwords=foo,bar,baz - lispwords=bar,baz,quux - bar,baz,quux - - Testing 'lispwords' value reset - lispwords=foo,bar,baz - lispwords=foo,bar,baz - foo,bar,baz]]) - end) -end) diff --git a/test/functional/legacy/101_hlsearch_spec.lua b/test/functional/legacy/101_hlsearch_spec.lua index 4b8b1cef50..335d275c2a 100644 --- a/test/functional/legacy/101_hlsearch_spec.lua +++ b/test/functional/legacy/101_hlsearch_spec.lua @@ -1,9 +1,8 @@ -- Test for v:hlsearch local helpers = require('test.functional.helpers') -local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert +local clear, feed = helpers.clear, helpers.feed local execute, expect = helpers.execute, helpers.expect -local eval = helpers.eval describe('v:hlsearch', function() setup(clear) diff --git a/test/functional/legacy/102_fnameescape_spec.lua b/test/functional/legacy/102_fnameescape_spec.lua index 251ce68430..a3b0313d7a 100644 --- a/test/functional/legacy/102_fnameescape_spec.lua +++ b/test/functional/legacy/102_fnameescape_spec.lua @@ -1,7 +1,7 @@ -- Test if fnameescape is correct for special chars like! local helpers = require('test.functional.helpers') -local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert +local clear = helpers.clear local execute, expect = helpers.execute, helpers.expect describe('fnameescape', function() diff --git a/test/functional/legacy/103_visual_mode_reset_spec.lua b/test/functional/legacy/103_visual_mode_reset_spec.lua index 6b2f3bc1b6..c1407ef10a 100644 --- a/test/functional/legacy/103_visual_mode_reset_spec.lua +++ b/test/functional/legacy/103_visual_mode_reset_spec.lua @@ -1,8 +1,8 @@ -- Test for visual mode not being reset causing E315 error. local helpers = require('test.functional.helpers') -local feed, insert, source = helpers.feed, helpers.insert, helpers.source -local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect +local feed, source = helpers.feed, helpers.source +local clear, expect = helpers.clear, helpers.expect describe('E315 error', function() setup(clear) diff --git a/test/functional/legacy/105_filename_modifiers_spec.lua b/test/functional/legacy/105_filename_modifiers_spec.lua deleted file mode 100644 index 3b417a88eb..0000000000 --- a/test/functional/legacy/105_filename_modifiers_spec.lua +++ /dev/null @@ -1,81 +0,0 @@ --- Test filename modifiers. - -local helpers = require('test.functional.helpers') -local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local execute, expect = helpers.execute, helpers.expect - -describe('filename modifiers', function() - setup(clear) - - it('is working', function() - local tmpdir = helpers.nvim('eval', 'resolve("/tmp")') - - execute('cd ' .. tmpdir) - execute([=[set shell=sh]=]) - execute([=[set shellslash]=]) - execute([=[let tab="\t"]=]) - execute([=[command -nargs=1 Put :let expr=<q-args> | $put =expr.tab.strtrans(string(eval(expr)))]=]) - execute([=[let $HOME=fnamemodify('.', ':p:h:h:h')]=]) - execute([=[Put fnamemodify('.', ':p' )[-1:]]=]) - execute([=[Put fnamemodify('.', ':p:h' )[-1:]]=]) - execute([=[Put fnamemodify('test.out', ':p' )[-1:]]=]) - execute([=[Put fnamemodify('test.out', ':.' )]=]) - execute([=[Put fnamemodify('../testdir/a', ':.' )]=]) - execute([=[Put fnamemodify('test.out', ':~' )]=]) - execute([=[Put fnamemodify('../testdir/a', ':~' )]=]) - execute([=[Put fnamemodify('../testdir/a', ':t' )]=]) - execute([=[Put fnamemodify('.', ':p:t' )]=]) - execute([=[Put fnamemodify('test.out', ':p:t' )]=]) - execute([=[Put fnamemodify('test.out', ':p:e' )]=]) - execute([=[Put fnamemodify('test.out', ':p:t:e' )]=]) - execute([=[Put fnamemodify('abc.fb2.tar.gz', ':r' )]=]) - execute([=[Put fnamemodify('abc.fb2.tar.gz', ':r:r' )]=]) - execute([=[Put fnamemodify('abc.fb2.tar.gz', ':r:r:r' )]=]) - execute([=[Put substitute(fnamemodify('abc.fb2.tar.gz', ':p:r:r'), '.*\(nvim/testdir/.*\)', '\1', '')]=]) - execute([=[Put fnamemodify('abc.fb2.tar.gz', ':e' )]=]) - execute([=[Put fnamemodify('abc.fb2.tar.gz', ':e:e' )]=]) - execute([=[Put fnamemodify('abc.fb2.tar.gz', ':e:e:e' )]=]) - execute([=[Put fnamemodify('abc.fb2.tar.gz', ':e:e:e:e')]=]) - execute([=[Put fnamemodify('abc.fb2.tar.gz', ':e:e:r' )]=]) - execute([=[Put fnamemodify('abc def', ':S' )]=]) - execute([=[Put fnamemodify('abc" "def', ':S' )]=]) - execute([=[Put fnamemodify('abc"%"def', ':S' )]=]) - execute([=[Put fnamemodify('abc'' ''def', ':S' )]=]) - execute([=[Put fnamemodify('abc''%''def', ':S' )]=]) - execute([=[Put fnamemodify("abc\ndef", ':S' )]=]) - execute([=[set shell=tcsh]=]) - execute([=[Put fnamemodify("abc\ndef", ':S' )]=]) - execute([=[1 delete _]=]) - - -- Assert buffer contents. - expect([=[ - fnamemodify('.', ':p' )[-1:] '/' - fnamemodify('.', ':p:h' )[-1:] 'p' - fnamemodify('test.out', ':p' )[-1:] 't' - fnamemodify('test.out', ':.' ) 'test.out' - fnamemodify('../testdir/a', ':.' ) '../testdir/a' - fnamemodify('test.out', ':~' ) 'test.out' - fnamemodify('../testdir/a', ':~' ) '../testdir/a' - fnamemodify('../testdir/a', ':t' ) 'a' - fnamemodify('.', ':p:t' ) '' - fnamemodify('test.out', ':p:t' ) 'test.out' - fnamemodify('test.out', ':p:e' ) 'out' - fnamemodify('test.out', ':p:t:e' ) 'out' - fnamemodify('abc.fb2.tar.gz', ':r' ) 'abc.fb2.tar' - fnamemodify('abc.fb2.tar.gz', ':r:r' ) 'abc.fb2' - fnamemodify('abc.fb2.tar.gz', ':r:r:r' ) 'abc' - substitute(fnamemodify('abc.fb2.tar.gz', ':p:r:r'), '.*\(nvim/testdir/.*\)', '\1', '') ']=] .. tmpdir .. [=[/abc.fb2' - fnamemodify('abc.fb2.tar.gz', ':e' ) 'gz' - fnamemodify('abc.fb2.tar.gz', ':e:e' ) 'tar.gz' - fnamemodify('abc.fb2.tar.gz', ':e:e:e' ) 'fb2.tar.gz' - fnamemodify('abc.fb2.tar.gz', ':e:e:e:e') 'fb2.tar.gz' - fnamemodify('abc.fb2.tar.gz', ':e:e:r' ) 'tar' - fnamemodify('abc def', ':S' ) '''abc def''' - fnamemodify('abc" "def', ':S' ) '''abc" "def''' - fnamemodify('abc"%"def', ':S' ) '''abc"%"def''' - fnamemodify('abc'' ''def', ':S' ) '''abc''\'''' ''\''''def''' - fnamemodify('abc''%''def', ':S' ) '''abc''\''''%''\''''def''' - fnamemodify("abc\ndef", ':S' ) '''abc^@def''' - fnamemodify("abc\ndef", ':S' ) '''abc\^@def''']=]) - end) -end) diff --git a/test/functional/legacy/106_errorformat_spec.lua b/test/functional/legacy/106_errorformat_spec.lua index 5b6037f928..5958f1aa7b 100644 --- a/test/functional/legacy/106_errorformat_spec.lua +++ b/test/functional/legacy/106_errorformat_spec.lua @@ -1,7 +1,7 @@ -- Tests for errorformat. local helpers = require('test.functional.helpers') -local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert +local clear = helpers.clear local execute, expect = helpers.execute, helpers.expect describe('errorformat', function() diff --git a/test/functional/legacy/107_adjust_window_and_contents_spec.lua b/test/functional/legacy/107_adjust_window_and_contents_spec.lua index f6ea7e5a6c..7a6de3d748 100644 --- a/test/functional/legacy/107_adjust_window_and_contents_spec.lua +++ b/test/functional/legacy/107_adjust_window_and_contents_spec.lua @@ -2,8 +2,8 @@ local helpers = require('test.functional.helpers') local Screen = require('test.functional.ui.screen') -local feed, insert, source = helpers.feed, helpers.insert, helpers.source -local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect +local insert = helpers.insert +local clear, execute = helpers.clear, helpers.execute describe('107', function() setup(clear) diff --git a/test/functional/legacy/assert_spec.lua b/test/functional/legacy/assert_spec.lua new file mode 100644 index 0000000000..1ce665360d --- /dev/null +++ b/test/functional/legacy/assert_spec.lua @@ -0,0 +1,145 @@ +local helpers = require('test.functional.helpers') +local nvim, call = helpers.meths, helpers.call +local clear, eq = helpers.clear, helpers.eq +local source, execute = helpers.source, helpers.execute + +local function expected_errors(errors) + eq(errors, nvim.get_vvar('errors')) +end + +local function expected_empty() + eq({}, nvim.get_vvar('errors')) +end + +describe('assert function:', function() + + before_each(function() + clear() + end) + + -- assert_equal({expected}, {actual}, [, {msg}]) + describe('assert_equal', function() + it('should not change v:errors when expected is equal to actual', function() + source([[ + let s = 'foo' + call assert_equal('foo', s) + let n = 4 + call assert_equal(4, n) + let l = [1, 2, 3] + call assert_equal([1, 2, 3], l) + fu Func() + endfu + let F1 = function('Func') + let F2 = function('Func') + call assert_equal(F1, F2) + ]]) + expected_empty() + end) + + it('should not change v:errors when expected is equal to actual', function() + call('assert_equal', '', '') + call('assert_equal', 'string', 'string') + expected_empty() + end) + + it('should change v:errors when expected is not equal to actual', function() + call('assert_equal', 0, {0}) + expected_errors({'Expected 0 but got [0]'}) + end) + + it('should change v:errors when expected is not equal to actual', function() + call('assert_equal', 0, "0") + expected_errors({"Expected 0 but got '0'"}) + end) + + it('should change v:errors when expected is not equal to actual', function() + -- Lua does not tell integer from float. + execute('call assert_equal(1, 1.0)') + expected_errors({'Expected 1 but got 1.0'}) + end) + + it('should change v:errors when expected is not equal to actual', function() + call('assert_equal', 'true', 'false') + expected_errors({"Expected 'true' but got 'false'"}) + end) + end) + + -- assert_false({actual}, [, {msg}]) + describe('assert_false', function() + it('should not change v:errors when actual is false', function() + call('assert_false', 0) + call('assert_false', false) + expected_empty() + end) + + it('should change v:errors when actual is not false', function() + call('assert_false', 1) + expected_errors({'Expected False but got 1'}) + end) + + it('should change v:errors when actual is not false', function() + call('assert_false', {}) + expected_errors({'Expected False but got []'}) + end) + end) + + -- assert_true({actual}, [, {msg}]) + describe('assert_true', function() + it('should not change v:errors when actual is true', function() + call('assert_true', 1) + call('assert_true', -1) -- In Vim script, non-zero Numbers are TRUE. + call('assert_true', true) + expected_empty() + end) + + it('should change v:errors when actual is not true', function() + call('assert_true', 1.5) + expected_errors({'Expected True but got 1.5'}) + end) + end) + + describe('v:errors', function() + it('should be initialized at startup', function() + expected_empty() + end) + + it('should have function names and relative line numbers', function() + source([[ + fu Func_one() + call assert_equal([0], {'0' : 0}) + call assert_false('False') + call assert_true("True") + endfu + fu Func_two() + " for shifting a line number + call assert_true('line two') + endfu + ]]) + call('Func_one') + call('Func_two') + expected_errors({ + "function Func_one line 1: Expected [0] but got {'0': 0}", + "function Func_one line 2: Expected False but got 'False'", + "function Func_one line 3: Expected True but got 'True'", + "function Func_two line 2: Expected True but got 'line two'", + }) + end) + + it('should have file names and passed messages', function() + local tmpname_one = source([[ + call assert_equal(1, 100, 'equal assertion failed') + call assert_false('true', 'true assertion failed') + call assert_true('false', 'false assertion failed') + ]]) + local tmpname_two = source([[ + call assert_true('', 'file two') + ]]) + expected_errors({ + tmpname_one .. " line 1: 'equal assertion failed'", + tmpname_one .. " line 2: 'true assertion failed'", + tmpname_one .. " line 3: 'false assertion failed'", + tmpname_two .. " line 1: 'file two'", + }) + end) + end) +end) diff --git a/test/functional/legacy/autocmd_option_spec.lua b/test/functional/legacy/autocmd_option_spec.lua new file mode 100644 index 0000000000..6349371808 --- /dev/null +++ b/test/functional/legacy/autocmd_option_spec.lua @@ -0,0 +1,318 @@ +local helpers = require('test.functional.helpers') +local nvim = helpers.meths +local clear, eq, neq = helpers.clear, helpers.eq, helpers.neq +local curbuf, buf = helpers.curbuf, helpers.bufmeths +local curwin = helpers.curwin +local redir_exec = helpers.redir_exec +local source, execute = helpers.source, helpers.execute + +local function declare_hook_function() + source([[ + fu! AutoCommand(match, bufnr, winnr) + let l:acc = { + \ 'option' : a:match, + \ 'oldval' : v:option_old, + \ 'newval' : v:option_new, + \ 'scope' : v:option_type, + \ 'attr' : { + \ 'bufnr' : a:bufnr, + \ 'winnr' : a:winnr, + \ } + \ } + call add(g:ret, l:acc) + endfu + ]]) +end + +local function set_hook(pattern) + execute( + 'au OptionSet ' + .. pattern .. + ' :call AutoCommand(expand("<amatch>"), bufnr("%"), winnr())' + ) +end + +local function init_var() + execute('let g:ret = []') +end + +local function get_result() + local ret = nvim.get_var('ret') + init_var() + return ret +end + +local function expected_table(option, oldval, newval, scope, attr) + return { + option = option, + oldval = tostring(oldval), + newval = tostring(newval), + scope = scope, + attr = attr, + } +end + +local function expected_combination(...) + local args = {...} + local ret = get_result() + + if not (#args == #ret) then + local expecteds = {} + for _, v in pairs(args) do + table.insert(expecteds, expected_table(unpack(v))) + end + eq(expecteds, ret) + return + end + + for i, v in ipairs(args) do + local attr = v[5] + if not attr then + -- remove attr entries + ret[i].attr = nil + else + -- remove attr entries which are not required + for k in pairs(ret[i].attr) do + if not attr[k] then + ret[i].attr[k] = nil + end + end + end + eq(expected_table(unpack(v)), ret[i]) + end +end + +local function expected_empty() + eq({}, get_result()) +end + +local function make_buffer() + local old_buf = curbuf() + execute('botright new') + local new_buf = curbuf() + execute('wincmd p') -- move previous window + + neq(old_buf, new_buf) + eq(old_buf, curbuf()) + + return new_buf +end + +local function get_new_window_number() + local old_win = curwin() + execute('botright new') + local new_win = curwin() + local new_winnr = redir_exec('echo winnr()') + execute('wincmd p') -- move previous window + + neq(old_win, new_win) + eq(old_win, curwin()) + + return new_winnr:gsub('\n', '') +end + +describe('au OptionSet', function() + describe('with any opton (*)', function() + + before_each(function() + clear() + declare_hook_function() + init_var() + set_hook('*') + end) + + it('should be called in setting number option', function() + execute('set nu') + expected_combination({'number', 0, 1, 'global'}) + + execute('setlocal nonu') + expected_combination({'number', 1, 0, 'local'}) + + execute('setglobal nonu') + expected_combination({'number', 1, 0, 'global'}) + end) + + it('should be called in setting autoindent option',function() + execute('setlocal ai') + expected_combination({'autoindent', 0, 1, 'local'}) + + execute('setglobal ai') + expected_combination({'autoindent', 0, 1, 'global'}) + + execute('set noai') + expected_combination({'autoindent', 1, 0, 'global'}) + end) + + it('should be called in inverting global autoindent option',function() + execute('set ai!') + expected_combination({'autoindent', 0, 1, 'global'}) + end) + + it('should be called in being unset local autoindent option',function() + execute('setlocal ai') + expected_combination({'autoindent', 0, 1, 'local'}) + + execute('setlocal ai<') + expected_combination({'autoindent', 1, 0, 'local'}) + end) + + it('should be called in setting global list and number option at the same time',function() + execute('set list nu') + expected_combination( + {'list', 0, 1, 'global'}, + {'number', 0, 1, 'global'} + ) + end) + + it('should not print anything, use :noa', function() + execute('noa set nolist nonu') + expected_empty() + end) + + it('should be called in setting local acd', function() + execute('setlocal acd') + expected_combination({'autochdir', 0, 1, 'local'}) + end) + + it('should be called in setting autoread', function() + execute('set noar') + expected_combination({'autoread', 1, 0, 'global'}) + + execute('setlocal ar') + expected_combination({'autoread', 0, 1, 'local'}) + end) + + it('should be called in inverting global autoread', function() + execute('setglobal invar') + expected_combination({'autoread', 1, 0, 'global'}) + end) + + it('should be called in setting backspace option through :let', function() + execute('let &bs=""') + expected_combination({'backspace', 'indent,eol,start', '', 'global'}) + end) + + describe('being set by setbufvar()', function() + it('should not trigger because option name is invalid', function() + execute('call setbufvar(1, "&l:bk", 1)') + expected_empty() + end) + + it('should trigger using correct option name', function() + execute('call setbufvar(1, "&backup", 1)') + expected_combination({'backup', 0, 1, 'local'}) + end) + + it('should trigger if the current buffer is different from the targetted buffer', function() + local new_buffer = make_buffer() + local new_bufnr = buf.get_number(new_buffer) + + execute('call setbufvar(' .. new_bufnr .. ', "&buftype", "nofile")') + expected_combination({'buftype', '', 'nofile', 'local', {bufnr = new_bufnr}}) + end) + end) + end) + + describe('with specific option', function() + + before_each(function() + clear() + declare_hook_function() + init_var() + end) + + it('should be called iff setting readonly', function() + set_hook('readonly') + + execute('set nu') + expected_empty() + + execute('setlocal ro') + expected_combination({'readonly', 0, 1, 'local'}) + + execute('setglobal ro') + expected_combination({'readonly', 0, 1, 'global'}) + + execute('set noro') + expected_combination({'readonly', 1, 0, 'global'}) + end) + + describe('being set by setbufvar()', function() + it('should not trigger because option name does not match with backup', function() + set_hook('backup') + + execute('call setbufvar(1, "&l:bk", 1)') + expected_empty() + end) + + it('should trigger, use correct option name backup', function() + set_hook('backup') + + execute('call setbufvar(1, "&backup", 1)') + expected_combination({'backup', 0, 1, 'local'}) + end) + + it('should trigger if the current buffer is different from the targetted buffer', function() + set_hook('buftype') + + local new_buffer = make_buffer() + local new_bufnr = buf.get_number(new_buffer) + + execute('call setbufvar(' .. new_bufnr .. ', "&buftype", "nofile")') + expected_combination({'buftype', '', 'nofile', 'local', {bufnr = new_bufnr}}) + end) + end) + + describe('being set by setwinvar()', function() + it('should not trigger because option name does not match with backup', function() + set_hook('backup') + + execute('call setwinvar(1, "&l:bk", 1)') + expected_empty() + end) + + it('should trigger, use correct option name backup', function() + set_hook('backup') + + execute('call setwinvar(1, "&backup", 1)') + expected_combination({'backup', 0, 1, 'local'}) + end) + + it('should not trigger if the current window is different from the targetted window', function() + set_hook('cursorcolumn') + + local new_winnr = get_new_window_number() + + execute('call setwinvar(' .. new_winnr .. ', "&cursorcolumn", 1)') + -- expected_combination({'cursorcolumn', 0, 1, 'local', {winnr = new_winnr}}) + expected_empty() + end) + end) + + describe('being set by neovim api', function() + it('should trigger if a boolean option be set globally', function() + set_hook('autochdir') + + nvim.set_option('autochdir', true) + eq(true, nvim.get_option('autochdir')) + expected_combination({'autochdir', '0', '1', 'global'}) + end) + + it('should trigger if a number option be set globally', function() + set_hook('cmdheight') + + nvim.set_option('cmdheight', 5) + eq(5, nvim.get_option('cmdheight')) + expected_combination({'cmdheight', 1, 5, 'global'}) + end) + + it('should trigger if a string option be set globally', function() + set_hook('ambiwidth') + + nvim.set_option('ambiwidth', 'double') + eq('double', nvim.get_option('ambiwidth')) + expected_combination({'ambiwidth', 'single', 'double', 'global'}) + end) + end) + end) +end) diff --git a/test/functional/legacy/backspace_opt_spec.lua b/test/functional/legacy/backspace_opt_spec.lua new file mode 100644 index 0000000000..b40019a410 --- /dev/null +++ b/test/functional/legacy/backspace_opt_spec.lua @@ -0,0 +1,67 @@ +local helpers = require('test.functional.helpers') +local call, clear = helpers.call, helpers.clear +local source, eq, nvim = helpers.source, helpers.eq, helpers.meths + +describe("test 'backspace' settings", function() + before_each(function() + clear() + + source([[ + func Exec(expr) + let str='' + try + exec a:expr + catch /.*/ + let str=v:exception + endtry + return str + endfunc + + func Test_backspace_option() + set backspace= + call assert_equal('', &backspace) + set backspace=indent + call assert_equal('indent', &backspace) + set backspace=eol + call assert_equal('eol', &backspace) + set backspace=start + call assert_equal('start', &backspace) + " Add the value + set backspace= + set backspace=indent + call assert_equal('indent', &backspace) + set backspace+=eol + call assert_equal('indent,eol', &backspace) + set backspace+=start + call assert_equal('indent,eol,start', &backspace) + " Delete the value + set backspace-=indent + call assert_equal('eol,start', &backspace) + set backspace-=start + call assert_equal('eol', &backspace) + set backspace-=eol + call assert_equal('', &backspace) + " Check the error + call assert_equal(0, match(Exec('set backspace=ABC'), '.*E474')) + call assert_equal(0, match(Exec('set backspace+=def'), '.*E474')) + " NOTE: Vim doesn't check following error... + "call assert_equal(0, match(Exec('set backspace-=ghi'), '.*E474')) + + " Check backwards compatibility with version 5.4 and earlier + set backspace=0 + call assert_equal('0', &backspace) + set backspace=1 + call assert_equal('1', &backspace) + set backspace=2 + call assert_equal('2', &backspace) + call assert_false(match(Exec('set backspace=3'), '.*E474')) + call assert_false(match(Exec('set backspace=10'), '.*E474')) + endfunc + ]]) + end) + + it('works', function() + call('Test_backspace_option') + eq({}, nvim.get_vvar('errors')) + end) +end) diff --git a/test/functional/legacy/cdo_spec.lua b/test/functional/legacy/cdo_spec.lua new file mode 100644 index 0000000000..4b313ede3f --- /dev/null +++ b/test/functional/legacy/cdo_spec.lua @@ -0,0 +1,226 @@ +-- Tests for the :cdo, :cfdo, :ldo and :lfdo commands + +local helpers = require('test.functional.helpers') +local nvim, clear = helpers.meths, helpers.clear +local call, feed = helpers.call, helpers.feed +local source, eq = helpers.source, helpers.eq + +local function expected_empty() + eq({}, nvim.get_vvar('errors')) +end + +describe('cdo', function() + before_each(function() + clear() + + call('writefile', {'Line1', 'Line2', 'Line3'}, 'Xtestfile1') + call('writefile', {'Line1', 'Line2', 'Line3'}, 'Xtestfile2') + call('writefile', {'Line1', 'Line2', 'Line3'}, 'Xtestfile3') + + source([=[ + " Returns the current line in '<filename> <linenum>L <column>C' format + function GetRuler() + return expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' + endfunction + + " Tests for the :cdo and :ldo commands + function XdoTests(cchar) + enew + + " Shortcuts for calling the cdo and ldo commands + let Xdo = a:cchar . 'do' + let Xgetexpr = a:cchar . 'getexpr' + let Xprev = a:cchar. 'prev' + let XdoCmd = Xdo . ' call add(l, GetRuler())' + + " Try with an empty list + let l = [] + exe XdoCmd + call assert_equal([], l) + + " Populate the list and then try + exe Xgetexpr . " ['non-error 1', 'Xtestfile1:1:3:Line1', 'non-error 2', 'Xtestfile2:2:2:Line2', 'non-error 3', 'Xtestfile3:3:1:Line3']" + + let l = [] + exe XdoCmd + call assert_equal(['Xtestfile1 1L 3C', 'Xtestfile2 2L 2C', 'Xtestfile3 3L 1C'], l) + + " Run command only on selected error lines + let l = [] + enew + exe "2,3" . XdoCmd + call assert_equal(['Xtestfile2 2L 2C', 'Xtestfile3 3L 1C'], l) + + " Boundary condition tests + let l = [] + enew + exe "1,1" . XdoCmd + call assert_equal(['Xtestfile1 1L 3C'], l) + + let l = [] + enew + exe "3" . XdoCmd + call assert_equal(['Xtestfile3 3L 1C'], l) + + " Range test commands + let l = [] + enew + exe "%" . XdoCmd + call assert_equal(['Xtestfile1 1L 3C', 'Xtestfile2 2L 2C', 'Xtestfile3 3L 1C'], l) + + let l = [] + enew + exe "1,$" . XdoCmd + call assert_equal(['Xtestfile1 1L 3C', 'Xtestfile2 2L 2C', 'Xtestfile3 3L 1C'], l) + + let l = [] + enew + exe Xprev + exe "." . XdoCmd + call assert_equal(['Xtestfile2 2L 2C'], l) + + let l = [] + enew + exe "+" . XdoCmd + call assert_equal(['Xtestfile3 3L 1C'], l) + + " Invalid error lines test + let l = [] + enew + exe "silent! 27" . XdoCmd + exe "silent! 4,5" . XdoCmd + call assert_equal([], l) + + " Run commands from an unsaved buffer + let v:errmsg='' + let l = [] + enew + setlocal modified + exe "silent! 2,2" . XdoCmd + if v:errmsg !~# 'No write since last change' + call add(v:errors, 'Unsaved file change test failed') + endif + + " If the executed command fails, then the operation should be aborted + enew! + let subst_count = 0 + exe "silent!" . Xdo . " s/Line/xLine/ | let subst_count += 1" + if subst_count != 1 || getline('.') != 'xLine1' + call add(v:errors, 'Abort command on error test failed') + endif + + let l = [] + exe "2,2" . Xdo . "! call add(l, GetRuler())" + call assert_equal(['Xtestfile2 2L 2C'], l) + + " List with no valid error entries + let l = [] + edit! +2 Xtestfile1 + exe Xgetexpr . " ['non-error 1', 'non-error 2', 'non-error 3']" + exe XdoCmd + call assert_equal([], l) + exe "silent! 2" . XdoCmd + call assert_equal([], l) + let v:errmsg='' + exe "%" . XdoCmd + exe "1,$" . XdoCmd + exe "." . XdoCmd + call assert_equal('', v:errmsg) + + " List with only one valid entry + let l = [] + exe Xgetexpr . " ['Xtestfile3:3:1:Line3']" + exe XdoCmd + call assert_equal(['Xtestfile3 3L 1C'], l) + + endfunction + + " Tests for the :cfdo and :lfdo commands + function XfdoTests(cchar) + enew + + " Shortcuts for calling the cfdo and lfdo commands + let Xfdo = a:cchar . 'fdo' + let Xgetexpr = a:cchar . 'getexpr' + let XfdoCmd = Xfdo . ' call add(l, GetRuler())' + let Xpfile = a:cchar. 'pfile' + + " Clear the quickfix/location list + exe Xgetexpr . " []" + + " Try with an empty list + let l = [] + exe XfdoCmd + call assert_equal([], l) + + " Populate the list and then try + exe Xgetexpr . " ['non-error 1', 'Xtestfile1:1:3:Line1', 'Xtestfile1:2:1:Line2', 'non-error 2', 'Xtestfile2:2:2:Line2', 'non-error 3', 'Xtestfile3:2:3:Line2', 'Xtestfile3:3:1:Line3']" + + let l = [] + exe XfdoCmd + call assert_equal(['Xtestfile1 1L 3C', 'Xtestfile2 2L 2C', 'Xtestfile3 2L 3C'], l) + + " Run command only on selected error lines + let l = [] + exe "2,3" . XfdoCmd + call assert_equal(['Xtestfile2 2L 2C', 'Xtestfile3 2L 3C'], l) + + " Boundary condition tests + let l = [] + exe "3" . XfdoCmd + call assert_equal(['Xtestfile3 2L 3C'], l) + + " Range test commands + let l = [] + exe "%" . XfdoCmd + call assert_equal(['Xtestfile1 1L 3C', 'Xtestfile2 2L 2C', 'Xtestfile3 2L 3C'], l) + + let l = [] + exe "1,$" . XfdoCmd + call assert_equal(['Xtestfile1 1L 3C', 'Xtestfile2 2L 2C', 'Xtestfile3 2L 3C'], l) + + let l = [] + exe Xpfile + exe "." . XfdoCmd + call assert_equal(['Xtestfile2 2L 2C'], l) + + " List with only one valid entry + let l = [] + exe Xgetexpr . " ['Xtestfile2:2:5:Line2']" + exe XfdoCmd + call assert_equal(['Xtestfile2 2L 5C'], l) + + endfunction + ]=]) + end) + + after_each(function() + os.remove('Xtestfile1') + os.remove('Xtestfile2') + os.remove('Xtestfile3') + end) + + it('works for :cdo', function() + -- call('XdoTests', 'c') + feed(":call XdoTests('c')<CR><C-l>") + expected_empty() + end) + + it('works for :cfdo', function() + -- call('XfdoTests', 'c') + feed(":call XfdoTests('c')<CR><C-l>") + expected_empty() + end) + + it('works for :ldo', function() + -- call('XdoTests', 'l') + feed(":call XdoTests('l')<CR><C-l>") + expected_empty() + end) + + it('works for :lfdo', function() + -- call('XfdoTests', 'l') + feed(":call XfdoTests('l')<CR><C-l>") + expected_empty() + end) +end) diff --git a/test/functional/legacy/charsearch_spec.lua b/test/functional/legacy/charsearch_spec.lua new file mode 100644 index 0000000000..4a83801cfc --- /dev/null +++ b/test/functional/legacy/charsearch_spec.lua @@ -0,0 +1,42 @@ +-- Test for character searches + +local helpers = require('test.functional.helpers') +local feed, insert = helpers.feed, helpers.insert +local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect + +describe('charsearch', function() + setup(clear) + + it('is working', function() + insert([[ + Xabcdefghijkemnopqretuvwxyz + Yabcdefghijkemnopqretuvwxyz + Zabcdefghijkemnokqretkvwxyz]]) + + -- Check that "fe" and ";" work. + execute('/^X') + feed('ylfep;;p,,p') + -- Check that save/restore works. + execute('/^Y') + feed('ylfep') + execute('let csave = getcharsearch()') + feed('fip') + execute('call setcharsearch(csave)') + feed(';p;p') + -- Check that setcharsearch() changes the settings. + execute('/^Z') + feed('ylfep') + execute("call setcharsearch({'char': 'k'})") + feed(';p') + execute("call setcharsearch({'forward': 0})") + feed('$;p') + execute("call setcharsearch({'until': 1})") + feed(';;p') + + -- Assert buffer contents. + expect([[ + XabcdeXfghijkeXmnopqreXtuvwxyz + YabcdeYfghiYjkeYmnopqreYtuvwxyz + ZabcdeZfghijkZZemnokqretkZvwxyz]]) + end) +end) diff --git a/test/functional/legacy/command_count_spec.lua b/test/functional/legacy/command_count_spec.lua new file mode 100644 index 0000000000..d9b4f09263 --- /dev/null +++ b/test/functional/legacy/command_count_spec.lua @@ -0,0 +1,243 @@ +-- Test for user command counts + +local helpers = require('test.functional.helpers') +local clear, source, expect = helpers.clear, helpers.source, helpers.expect +local execute, spawn = helpers.execute, helpers.spawn +local nvim_prog = helpers.nvim_prog + +describe('command_count', function() + setup(clear) + teardown(function() + os.remove('test.out') + end) + + it('is working', function() + -- It is relevant for the test to load a file initially. If this is + -- emulated with :arg the buffer count is wrong as nvim creates an empty + -- buffer if it was started without a filename. + local nvim2 = spawn({nvim_prog, '-u', 'NONE', '-i', 'NONE', '--embed', + 'test_command_count.in'}) + helpers.set_session(nvim2) + + source([[ + lang C + let g:lines = [] + com -range=% RangeLines + \ :call add(g:lines, 'RangeLines '.<line1>.' '.<line2>) + com -range -addr=arguments RangeArguments + \ :call add(g:lines, 'RangeArguments '.<line1>.' '.<line2>) + com -range=% -addr=arguments RangeArgumentsAll + \ :call add(g:lines, 'RangeArgumentsAll '.<line1>.' '.<line2>) + com -range -addr=loaded_buffers RangeLoadedBuffers + \ :call add(g:lines, 'RangeLoadedBuffers '.<line1>.' '.<line2>) + com -range=% -addr=loaded_buffers RangeLoadedBuffersAll + \ :call add(g:lines, 'RangeLoadedBuffersAll '.<line1>.' '.<line2>) + com -range -addr=buffers RangeBuffers + \ :call add(g:lines, 'RangeBuffers '.<line1>.' '.<line2>) + com -range=% -addr=buffers RangeBuffersAll + \ :call add(g:lines, 'RangeBuffersAll '.<line1>.' '.<line2>) + com -range -addr=windows RangeWindows + \ :call add(g:lines, 'RangeWindows '.<line1>.' '.<line2>) + com -range=% -addr=windows RangeWindowsAll + \ :call add(g:lines, 'RangeWindowsAll '.<line1>.' '.<line2>) + com -range -addr=tabs RangeTabs + \ :call add(g:lines, 'RangeTabs '.<line1>.' '.<line2>) + com -range=% -addr=tabs RangeTabsAll + \ :call add(g:lines, 'RangeTabsAll '.<line1>.' '.<line2>) + set hidden + arga a b c d + argdo echo "loading buffers" + argu 3 + .-,$-RangeArguments + %RangeArguments + RangeArgumentsAll + N + .RangeArguments + split + split + split + split + 3wincmd w + .,$RangeWindows + %RangeWindows + RangeWindowsAll + only + blast + bd + .,$RangeLoadedBuffers + %RangeLoadedBuffers + RangeLoadedBuffersAll + .,$RangeBuffers + %RangeBuffers + RangeBuffersAll + tabe + tabe + tabe + tabe + normal 2gt + .,$RangeTabs + %RangeTabs + RangeTabsAll + 1tabonly + s/\n/\r\r\r\r\r/ + 2ma< + $-ma> + '<,'>RangeLines + com -range=% -buffer LocalRangeLines + \ :call add(g:lines, 'LocalRangeLines '.<line1>.' '.<line2>) + '<,'>LocalRangeLines + b1 + call add(g:lines, '') + %argd + arga a b c d + ]]) + -- This can not be in the source() call as it will produce errors. + execute([[let v:errmsg = '']]) + execute('5argu') + execute([[call add(g:lines, '5argu ' . v:errmsg)]]) + execute('$argu') + execute([[call add(g:lines, '4argu ' . expand('%:t'))]]) + execute([[let v:errmsg = '']]) + execute('1argu') + execute([[call add(g:lines, '1argu ' . expand('%:t'))]]) + execute([[let v:errmsg = '']]) + execute('100b') + execute([[call add(g:lines, '100b ' . v:errmsg)]]) + execute('split') + execute('split') + execute('split') + execute('split') + execute([[let v:errmsg = '']]) + execute('0close') + execute([[call add(g:lines, '0close ' . v:errmsg)]]) + execute('$wincmd w') + execute('$close') + execute([[call add(g:lines, '$close ' . winnr())]]) + execute([[let v:errmsg = '']]) + execute('$+close') + execute([[call add(g:lines, '$+close ' . v:errmsg)]]) + execute('$tabe') + execute([[call add(g:lines, '$tabe ' . tabpagenr())]]) + execute([[let v:errmsg = '']]) + execute('$+tabe') + execute([[call add(g:lines, '$+tabe ' . v:errmsg)]]) + source([[ + only! + e x + 0tabm + normal 1gt + call add(g:lines, '0tabm ' . expand('%:t')) + tabonly! + only! + e! test.out + call append(0, g:lines) + unlet g:lines + w + bd + b1 + let g:lines = [] + func BufStatus() + call add(g:lines, + \ 'aaa: ' . buflisted(g:buf_aaa) . + \ ' bbb: ' . buflisted(g:buf_bbb) . + \ ' ccc: ' . buflisted(g:buf_ccc)) + endfunc + se nohidden + e aaa + let buf_aaa = bufnr('%') + e bbb + let buf_bbb = bufnr('%') + e ccc + let buf_ccc = bufnr('%') + b1 + call BufStatus() + exe buf_bbb . "," . buf_ccc . "bdelete" + call BufStatus() + exe buf_aaa . "bdelete" + call BufStatus() + e! test.out + call append('$', g:lines) + unlet g:lines + delfunc BufStatus + w + bd + b1 + se hidden + only! + let g:lines = [] + %argd + arga a b c d e f + 3argu + let args = '' + .,$-argdo let args .= ' '.expand('%') + call add(g:lines, 'argdo:' . args) + split + split + split + split + 2wincmd w + let windows = '' + .,$-windo let windows .= ' '.winnr() + call add(g:lines, 'windo:'. windows) + b2 + let buffers = '' + .,$-bufdo let buffers .= ' '.bufnr('%') + call add(g:lines, 'bufdo:' . buffers) + 3bd + let buffers = '' + 3,7bufdo let buffers .= ' '.bufnr('%') + call add(g:lines, 'bufdo:' . buffers) + tabe + tabe + tabe + tabe + normal! 2gt + let tabpages = '' + .,$-tabdo let tabpages .= ' '.tabpagenr() + call add(g:lines, 'tabdo:' . tabpages) + e! test.out + call append('$', g:lines) + ]]) + + -- Assert buffer contents. + expect([[ + RangeArguments 2 4 + RangeArguments 1 5 + RangeArgumentsAll 1 5 + RangeArguments 2 2 + RangeWindows 3 5 + RangeWindows 1 5 + RangeWindowsAll 1 5 + RangeLoadedBuffers 2 4 + RangeLoadedBuffers 1 4 + RangeLoadedBuffersAll 1 4 + RangeBuffers 2 5 + RangeBuffers 1 5 + RangeBuffersAll 1 5 + RangeTabs 2 5 + RangeTabs 1 5 + RangeTabsAll 1 5 + RangeLines 2 5 + LocalRangeLines 2 5 + + 5argu E16: Invalid range + 4argu d + 1argu a + 100b E16: Invalid range + 0close + $close 3 + $+close E16: Invalid range + $tabe 2 + $+tabe E16: Invalid range + 0tabm x + + aaa: 1 bbb: 1 ccc: 1 + aaa: 1 bbb: 0 ccc: 0 + aaa: 0 bbb: 0 ccc: 0 + argdo: c d e + windo: 2 3 4 + bufdo: 2 3 4 5 6 7 8 9 10 15 + bufdo: 4 5 6 7 + tabdo: 2 3 4]]) + end) +end) diff --git a/test/functional/legacy/comparators_spec.lua b/test/functional/legacy/comparators_spec.lua new file mode 100644 index 0000000000..e3fa3eea23 --- /dev/null +++ b/test/functional/legacy/comparators_spec.lua @@ -0,0 +1,14 @@ +-- " Test for expression comparators. + +local helpers = require('test.functional.helpers') +local clear, eq = helpers.clear, helpers.eq +local eval, execute = helpers.eval, helpers.execute + +describe('comparators', function() + before_each(clear) + + it('is working', function() + execute('set isident+=#') + eq(1, eval('1 is#1')) + end) +end) diff --git a/test/functional/legacy/delete_spec.lua b/test/functional/legacy/delete_spec.lua new file mode 100644 index 0000000000..cd18a8f750 --- /dev/null +++ b/test/functional/legacy/delete_spec.lua @@ -0,0 +1,99 @@ +local helpers = require('test.functional.helpers') +local clear, source = helpers.clear, helpers.source +local eq, eval, execute = helpers.eq, helpers.eval, helpers.execute + +describe('Test for delete()', function() + before_each(clear) + + it('file delete', function() + execute('split Xfile') + execute("call setline(1, ['a', 'b'])") + execute('wq') + eq(eval("['a', 'b']"), eval("readfile('Xfile')")) + eq(0, eval("delete('Xfile')")) + eq(-1, eval("delete('Xfile')")) + end) + + it('directory delete', function() + execute("call mkdir('Xdir1')") + eq(1, eval("isdirectory('Xdir1')")) + eq(0, eval("delete('Xdir1', 'd')")) + eq(0, eval("isdirectory('Xdir1')")) + eq(-1, eval("delete('Xdir1', 'd')")) + end) + it('recursive delete', function() + execute("call mkdir('Xdir1')") + execute("call mkdir('Xdir1/subdir')") + execute("call mkdir('Xdir1/empty')") + execute('split Xdir1/Xfile') + execute("call setline(1, ['a', 'b'])") + execute('w') + execute('w Xdir1/subdir/Xfile') + execute('close') + + eq(1, eval("isdirectory('Xdir1')")) + eq(eval("['a', 'b']"), eval("readfile('Xdir1/Xfile')")) + eq(1, eval("isdirectory('Xdir1/subdir')")) + eq(eval("['a', 'b']"), eval("readfile('Xdir1/subdir/Xfile')")) + eq(1, eval("isdirectory('Xdir1/empty')")) + eq(0, eval("delete('Xdir1', 'rf')")) + eq(0, eval("isdirectory('Xdir1')")) + eq(-1, eval("delete('Xdir1', 'd')")) + end) + + it('symlink delete', function() + source([[ + split Xfile + call setline(1, ['a', 'b']) + wq + silent !ln -s Xfile Xlink + ]]) + -- Delete the link, not the file + eq(0, eval("delete('Xlink')")) + eq(-1, eval("delete('Xlink')")) + eq(0, eval("delete('Xfile')")) + end) + + it('symlink directory delete', function() + execute("call mkdir('Xdir1')") + execute("silent !ln -s Xdir1 Xlink") + eq(1, eval("isdirectory('Xdir1')")) + eq(1, eval("isdirectory('Xlink')")) + -- Delete the link, not the directory + eq(0, eval("delete('Xlink')")) + eq(-1, eval("delete('Xlink')")) + eq(0, eval("delete('Xdir1', 'd')")) + end) + + it('symlink recursive delete', function() + source([[ + call mkdir('Xdir3') + call mkdir('Xdir3/subdir') + call mkdir('Xdir4') + split Xdir3/Xfile + call setline(1, ['a', 'b']) + w + w Xdir3/subdir/Xfile + w Xdir4/Xfile + close + silent !ln -s ../Xdir4 Xdir3/Xlink + ]]) + + eq(1, eval("isdirectory('Xdir3')")) + eq(eval("['a', 'b']"), eval("readfile('Xdir3/Xfile')")) + eq(1, eval("isdirectory('Xdir3/subdir')")) + eq(eval("['a', 'b']"), eval("readfile('Xdir3/subdir/Xfile')")) + eq(1, eval("isdirectory('Xdir4')")) + eq(1, eval("isdirectory('Xdir3/Xlink')")) + eq(eval("['a', 'b']"), eval("readfile('Xdir4/Xfile')")) + + eq(0, eval("delete('Xdir3', 'rf')")) + eq(0, eval("isdirectory('Xdir3')")) + eq(-1, eval("delete('Xdir3', 'd')")) + -- symlink is deleted, not the directory it points to + eq(1, eval("isdirectory('Xdir4')")) + eq(eval("['a', 'b']"), eval("readfile('Xdir4/Xfile')")) + eq(0, eval("delete('Xdir4/Xfile')")) + eq(0, eval("delete('Xdir4', 'd')")) + end) +end) diff --git a/test/functional/legacy/erasebackword_spec.lua b/test/functional/legacy/erasebackword_spec.lua new file mode 100644 index 0000000000..cb3967b763 --- /dev/null +++ b/test/functional/legacy/erasebackword_spec.lua @@ -0,0 +1,24 @@ +-- Test for CTRL-W in Insert mode + +local helpers = require('test.functional.helpers') +local clear, feed, expect = helpers.clear, helpers.feed, helpers.expect + +describe('CTRL-W in Insert mode', function() + setup(clear) + + it('works for multi-byte characters', function() + + for i = 1, 6 do + feed('o wwwこんにちわ世界ワールドvim ' .. string.rep('<C-w>', i) .. '<esc>') + end + + expect([[ + + wwwこんにちわ世界ワールド + wwwこんにちわ世界 + wwwこんにちわ + www + + ]]) + end) +end) diff --git a/test/functional/legacy/eval_spec.lua b/test/functional/legacy/eval_spec.lua new file mode 100644 index 0000000000..3ff1092a4b --- /dev/null +++ b/test/functional/legacy/eval_spec.lua @@ -0,0 +1,736 @@ +-- Test for various eval features. + +local helpers = require('test.functional.helpers') +local feed, insert, source = helpers.feed, helpers.insert, helpers.source +local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect +local eq, eval, write_file = helpers.eq, helpers.eval, helpers.write_file + +local function has_clipboard() + clear() + return 1 == eval("has('clipboard')") +end + +describe('eval', function() + setup(function() + write_file('test_eval_setup.vim', [[ + set noswapfile + lang C + + fun AppendRegContents(reg) + call AppendRegParts(a:reg, getregtype(a:reg), getreg(a:reg), string(getreg(a:reg, 0, 1)), getreg(a:reg, 1), string(getreg(a:reg, 1, 1))) + endfun + + fun AppendRegParts(reg, type, cont, strcont, cont1, strcont1) + call append('$', printf('%s: type %s; value: %s (%s), expr: %s (%s)', a:reg, a:type, a:cont, a:strcont, a:cont1, a:strcont1)) + endfun + + command -nargs=? AR :call AppendRegContents(<q-args>) + + fun SetReg(...) + call call('setreg', a:000) + call append('$', printf('{{{2 setreg(%s)', string(a:000)[1:-2])) + call AppendRegContents(a:1) + if a:1 isnot# '=' + execute "silent normal! Go==\n==\e\"".a:1."P" + endif + endfun + ]]) + end) + before_each(clear) + teardown(function() + os.remove('test_eval_setup.vim') + end) + + it(':let', function() + execute('so test_eval_setup.vim') + execute([[let @" = 'abc']]) + execute('AR "') + execute([[let @" = "abc\n"]]) + source('AR "') + execute([[let @" = "abc\<C-m>"]]) + execute('AR "') + execute([[let @= = '"abc"']]) + execute('AR =') + expect([[ + + ": type v; value: abc (['abc']), expr: abc (['abc']) + ": type V; value: abc]].."\000 (['abc']), expr: abc\000"..[[ (['abc']) + ": type V; value: abc]].."\r\000 (['abc\r']), expr: abc\r\000 (['abc\r"..[[']) + =: type v; value: abc (['abc']), expr: "abc" (['"abc"'])]]) + end) + + it('basic setreg() tests', function() + execute('so test_eval_setup.vim') + insert('{{{1 Basic setreg tests') + execute([[call SetReg('a', 'abcA', 'c')]]) + execute([[call SetReg('b', 'abcB', 'v')]]) + execute([[call SetReg('c', 'abcC', 'l')]]) + execute([[call SetReg('d', 'abcD', 'V')]]) + execute([[call SetReg('e', 'abcE', 'b')]]) + execute([[call SetReg('f', 'abcF', "\<C-v>")]]) + execute([[call SetReg('g', 'abcG', 'b10')]]) + execute([[call SetReg('h', 'abcH', "\<C-v>10")]]) + execute([[call SetReg('I', 'abcI')]]) + + feed('Go{{{1 Appending single lines with setreg()<esc>') + execute([[call SetReg('A', 'abcAc', 'c')]]) + execute([[call SetReg('A', 'abcAl', 'l')]]) + execute([[call SetReg('A', 'abcAc2','c')]]) + execute([[call SetReg('b', 'abcBc', 'ca')]]) + execute([[call SetReg('b', 'abcBb', 'ba')]]) + execute([[call SetReg('b', 'abcBc2','ca')]]) + execute([[call SetReg('b', 'abcBb2','b50a')]]) + execute([[call SetReg('C', 'abcCl', 'l')]]) + execute([[call SetReg('C', 'abcCc', 'c')]]) + execute([[call SetReg('D', 'abcDb', 'b')]]) + execute([[call SetReg('E', 'abcEb', 'b')]]) + execute([[call SetReg('E', 'abcEl', 'l')]]) + execute([[call SetReg('F', 'abcFc', 'c')]]) + expect([[ + {{{1 Basic setreg tests + {{{2 setreg('a', 'abcA', 'c') + a: type v; value: abcA (['abcA']), expr: abcA (['abcA']) + == + =abcA= + {{{2 setreg('b', 'abcB', 'v') + b: type v; value: abcB (['abcB']), expr: abcB (['abcB']) + == + =abcB= + {{{2 setreg('c', 'abcC', 'l') + c: type V; value: abcC]].."\000 (['abcC']), expr: abcC\000"..[[ (['abcC']) + == + abcC + == + {{{2 setreg('d', 'abcD', 'V') + d: type V; value: abcD]].."\000 (['abcD']), expr: abcD\000"..[[ (['abcD']) + == + abcD + == + {{{2 setreg('e', 'abcE', 'b') + e: type ]]..'\022'..[[4; value: abcE (['abcE']), expr: abcE (['abcE']) + == + =abcE= + {{{2 setreg('f', 'abcF', ']]..'\022'..[[') + f: type ]]..'\022'..[[4; value: abcF (['abcF']), expr: abcF (['abcF']) + == + =abcF= + {{{2 setreg('g', 'abcG', 'b10') + g: type ]]..'\022'..[[10; value: abcG (['abcG']), expr: abcG (['abcG']) + == + =abcG = + {{{2 setreg('h', 'abcH', ']]..'\022'..[[10') + h: type ]]..'\022'..[[10; value: abcH (['abcH']), expr: abcH (['abcH']) + == + =abcH = + {{{2 setreg('I', 'abcI') + I: type v; value: abcI (['abcI']), expr: abcI (['abcI']) + == + =abcI= + {{{1 Appending single lines with setreg() + {{{2 setreg('A', 'abcAc', 'c') + A: type v; value: abcAabcAc (['abcAabcAc']), expr: abcAabcAc (['abcAabcAc']) + == + =abcAabcAc= + {{{2 setreg('A', 'abcAl', 'l') + A: type V; value: abcAabcAcabcAl]].."\000 (['abcAabcAcabcAl']), expr: abcAabcAcabcAl\000"..[[ (['abcAabcAcabcAl']) + == + abcAabcAcabcAl + == + {{{2 setreg('A', 'abcAc2', 'c') + A: type v; value: abcAabcAcabcAl]].."\000abcAc2 (['abcAabcAcabcAl', 'abcAc2']), expr: abcAabcAcabcAl\000"..[[abcAc2 (['abcAabcAcabcAl', 'abcAc2']) + == + =abcAabcAcabcAl + abcAc2= + {{{2 setreg('b', 'abcBc', 'ca') + b: type v; value: abcBabcBc (['abcBabcBc']), expr: abcBabcBc (['abcBabcBc']) + == + =abcBabcBc= + {{{2 setreg('b', 'abcBb', 'ba') + b: type ]]..'\022'..[[5; value: abcBabcBcabcBb (['abcBabcBcabcBb']), expr: abcBabcBcabcBb (['abcBabcBcabcBb']) + == + =abcBabcBcabcBb= + {{{2 setreg('b', 'abcBc2', 'ca') + b: type v; value: abcBabcBcabcBb]].."\000abcBc2 (['abcBabcBcabcBb', 'abcBc2']), expr: abcBabcBcabcBb\000"..[[abcBc2 (['abcBabcBcabcBb', 'abcBc2']) + == + =abcBabcBcabcBb + abcBc2= + {{{2 setreg('b', 'abcBb2', 'b50a') + b: type ]].."\02250; value: abcBabcBcabcBb\000abcBc2abcBb2 (['abcBabcBcabcBb', 'abcBc2abcBb2']), expr: abcBabcBcabcBb\000"..[[abcBc2abcBb2 (['abcBabcBcabcBb', 'abcBc2abcBb2']) + == + =abcBabcBcabcBb = + abcBc2abcBb2 + {{{2 setreg('C', 'abcCl', 'l') + C: type V; value: abcC]].."\000abcCl\000 (['abcC', 'abcCl']), expr: abcC\000abcCl\000"..[[ (['abcC', 'abcCl']) + == + abcC + abcCl + == + {{{2 setreg('C', 'abcCc', 'c') + C: type v; value: abcC]].."\000abcCl\000abcCc (['abcC', 'abcCl', 'abcCc']), expr: abcC\000abcCl\000"..[[abcCc (['abcC', 'abcCl', 'abcCc']) + == + =abcC + abcCl + abcCc= + {{{2 setreg('D', 'abcDb', 'b') + D: type ]].."\0225; value: abcD\000abcDb (['abcD', 'abcDb']), expr: abcD\000"..[[abcDb (['abcD', 'abcDb']) + == + =abcD = + abcDb + {{{2 setreg('E', 'abcEb', 'b') + E: type ]].."\0225; value: abcE\000abcEb (['abcE', 'abcEb']), expr: abcE\000"..[[abcEb (['abcE', 'abcEb']) + == + =abcE = + abcEb + {{{2 setreg('E', 'abcEl', 'l') + E: type V; value: abcE]].."\000abcEb\000abcEl\000 (['abcE', 'abcEb', 'abcEl']), expr: abcE\000abcEb\000abcEl\000"..[[ (['abcE', 'abcEb', 'abcEl']) + == + abcE + abcEb + abcEl + == + {{{2 setreg('F', 'abcFc', 'c') + F: type v; value: abcF]].."\000abcFc (['abcF', 'abcFc']), expr: abcF\000"..[[abcFc (['abcF', 'abcFc']) + == + =abcF + abcFc=]]) + end) + + it('appending NL with setreg()', function() + execute('so test_eval_setup.vim') + + execute([[call setreg('a', 'abcA2', 'c')]]) + execute([[call setreg('b', 'abcB2', 'v')]]) + execute([[call setreg('c', 'abcC2', 'l')]]) + execute([[call setreg('d', 'abcD2', 'V')]]) + execute([[call setreg('e', 'abcE2', 'b')]]) + execute([[call setreg('f', 'abcF2', "\<C-v>")]]) + -- These registers where set like this in the old test_eval.in but never + -- copied to the output buffer with SetReg(). They do not appear in + -- test_eval.ok. Therefore they are commented out. + --execute([[call setreg('g', 'abcG2', 'b10')]]) + --execute([[call setreg('h', 'abcH2', "\<C-v>10")]]) + --execute([[call setreg('I', 'abcI2')]]) + + execute([[call SetReg('A', "\n")]]) + execute([[call SetReg('B', "\n", 'c')]]) + execute([[call SetReg('C', "\n")]]) + execute([[call SetReg('D', "\n", 'l')]]) + execute([[call SetReg('E', "\n")]]) + execute([[call SetReg('F', "\n", 'b')]]) + expect([[ + + {{{2 setreg('A', ']]..'\000'..[[') + A: type V; value: abcA2]].."\000 (['abcA2']), expr: abcA2\000"..[[ (['abcA2']) + == + abcA2 + == + {{{2 setreg('B', ']]..'\000'..[[', 'c') + B: type v; value: abcB2]].."\000 (['abcB2', '']), expr: abcB2\000"..[[ (['abcB2', '']) + == + =abcB2 + = + {{{2 setreg('C', ']]..'\000'..[[') + C: type V; value: abcC2]].."\000\000 (['abcC2', '']), expr: abcC2\000\000"..[[ (['abcC2', '']) + == + abcC2 + + == + {{{2 setreg('D', ']]..'\000'..[[', 'l') + D: type V; value: abcD2]].."\000\000 (['abcD2', '']), expr: abcD2\000\000"..[[ (['abcD2', '']) + == + abcD2 + + == + {{{2 setreg('E', ']]..'\000'..[[') + E: type V; value: abcE2]].."\000\000 (['abcE2', '']), expr: abcE2\000\000"..[[ (['abcE2', '']) + == + abcE2 + + == + {{{2 setreg('F', ']]..'\000'..[[', 'b') + F: type ]].."\0220; value: abcF2\000 (['abcF2', '']), expr: abcF2\000"..[[ (['abcF2', '']) + == + =abcF2= + ]]) + end) + + it('setting and appending list with setreg()', function() + execute('so test_eval_setup.vim') + + execute([[$put ='{{{1 Setting lists with setreg()']]) + execute([=[call SetReg('a', ['abcA3'], 'c')]=]) + execute([=[call SetReg('b', ['abcB3'], 'l')]=]) + execute([=[call SetReg('c', ['abcC3'], 'b')]=]) + execute([=[call SetReg('d', ['abcD3'])]=]) + execute([=[call SetReg('e', [1, 2, 'abc', 3])]=]) + execute([=[call SetReg('f', [1, 2, 3])]=]) + + execute([[$put ='{{{1 Appending lists with setreg()']]) + execute([=[call SetReg('A', ['abcA3c'], 'c')]=]) + execute([=[call SetReg('b', ['abcB3l'], 'la')]=]) + execute([=[call SetReg('C', ['abcC3b'], 'lb')]=]) + execute([=[call SetReg('D', ['abcD32'])]=]) + execute([=[call SetReg('A', ['abcA32'])]=]) + execute([=[call SetReg('B', ['abcB3c'], 'c')]=]) + execute([=[call SetReg('C', ['abcC3l'], 'l')]=]) + execute([=[call SetReg('D', ['abcD3b'], 'b')]=]) + expect([[ + + {{{1 Setting lists with setreg() + {{{2 setreg('a', ['abcA3'], 'c') + a: type v; value: abcA3 (['abcA3']), expr: abcA3 (['abcA3']) + == + =abcA3= + {{{2 setreg('b', ['abcB3'], 'l') + b: type V; value: abcB3]].."\000 (['abcB3']), expr: abcB3\000"..[[ (['abcB3']) + == + abcB3 + == + {{{2 setreg('c', ['abcC3'], 'b') + c: type ]]..'\022'..[[5; value: abcC3 (['abcC3']), expr: abcC3 (['abcC3']) + == + =abcC3= + {{{2 setreg('d', ['abcD3']) + d: type V; value: abcD3]].."\000 (['abcD3']), expr: abcD3\000"..[[ (['abcD3']) + == + abcD3 + == + {{{2 setreg('e', [1, 2, 'abc', 3]) + e: type V; value: 1]].."\0002\000abc\0003\000 (['1', '2', 'abc', '3']), expr: 1\0002\000abc\0003\000"..[[ (['1', '2', 'abc', '3']) + == + 1 + 2 + abc + 3 + == + {{{2 setreg('f', [1, 2, 3]) + f: type V; value: 1]].."\0002\0003\000 (['1', '2', '3']), expr: 1\0002\0003\000"..[[ (['1', '2', '3']) + == + 1 + 2 + 3 + == + {{{1 Appending lists with setreg() + {{{2 setreg('A', ['abcA3c'], 'c') + A: type v; value: abcA3]].."\000abcA3c (['abcA3', 'abcA3c']), expr: abcA3\000"..[[abcA3c (['abcA3', 'abcA3c']) + == + =abcA3 + abcA3c= + {{{2 setreg('b', ['abcB3l'], 'la') + b: type V; value: abcB3]].."\000abcB3l\000 (['abcB3', 'abcB3l']), expr: abcB3\000abcB3l\000"..[[ (['abcB3', 'abcB3l']) + == + abcB3 + abcB3l + == + {{{2 setreg('C', ['abcC3b'], 'lb') + C: type ]].."\0226; value: abcC3\000abcC3b (['abcC3', 'abcC3b']), expr: abcC3\000"..[[abcC3b (['abcC3', 'abcC3b']) + == + =abcC3 = + abcC3b + {{{2 setreg('D', ['abcD32']) + D: type V; value: abcD3]].."\000abcD32\000 (['abcD3', 'abcD32']), expr: abcD3\000abcD32\000"..[[ (['abcD3', 'abcD32']) + == + abcD3 + abcD32 + == + {{{2 setreg('A', ['abcA32']) + A: type V; value: abcA3]].."\000abcA3c\000abcA32\000 (['abcA3', 'abcA3c', 'abcA32']), expr: abcA3\000abcA3c\000abcA32\000"..[[ (['abcA3', 'abcA3c', 'abcA32']) + == + abcA3 + abcA3c + abcA32 + == + {{{2 setreg('B', ['abcB3c'], 'c') + B: type v; value: abcB3]].."\000abcB3l\000abcB3c (['abcB3', 'abcB3l', 'abcB3c']), expr: abcB3\000abcB3l\000"..[[abcB3c (['abcB3', 'abcB3l', 'abcB3c']) + == + =abcB3 + abcB3l + abcB3c= + {{{2 setreg('C', ['abcC3l'], 'l') + C: type V; value: abcC3]].."\000abcC3b\000abcC3l\000 (['abcC3', 'abcC3b', 'abcC3l']), expr: abcC3\000abcC3b\000abcC3l\000"..[[ (['abcC3', 'abcC3b', 'abcC3l']) + == + abcC3 + abcC3b + abcC3l + == + {{{2 setreg('D', ['abcD3b'], 'b') + D: type ]].."\0226; value: abcD3\000abcD32\000abcD3b (['abcD3', 'abcD32', 'abcD3b']), expr: abcD3\000abcD32\000"..[[abcD3b (['abcD3', 'abcD32', 'abcD3b']) + == + =abcD3 = + abcD32 + abcD3b]]) + + -- From now on we delete the buffer contents after each expect() to make + -- the next expect() easier to write. This is neccessary because null + -- bytes on a line by itself don't play well together with the dedent + -- function used in expect(). + execute('%delete') + execute([[$put ='{{{1 Appending lists with NL with setreg()']]) + execute([=[call SetReg('A', ["\n", 'abcA3l2'], 'l')]=]) + expect( + '\n'.. + '{{{1 Appending lists with NL with setreg()\n'.. + "{{{2 setreg('A', ['\000', 'abcA3l2'], 'l')\n".. + "A: type V; value: abcA3\000abcA3c\000abcA32\000\000\000abcA3l2\000 (['abcA3', 'abcA3c', 'abcA32', '\000', 'abcA3l2']), expr: abcA3\000abcA3c\000abcA32\000\000\000abcA3l2\000 (['abcA3', 'abcA3c', 'abcA32', '\000', 'abcA3l2'])\n".. + '==\n'.. + 'abcA3\n'.. + 'abcA3c\n'.. + 'abcA32\n'.. + '\000\n'.. + 'abcA3l2\n'.. + '==') + execute('%delete') + execute([=[call SetReg('B', ["\n", 'abcB3c2'], 'c')]=]) + expect( + '\n'.. + "{{{2 setreg('B', ['\000', 'abcB3c2'], 'c')\n".. + "B: type v; value: abcB3\000abcB3l\000abcB3c\000\000\000abcB3c2 (['abcB3', 'abcB3l', 'abcB3c', '\000', 'abcB3c2']), expr: abcB3\000abcB3l\000abcB3c\000\000\000abcB3c2 (['abcB3', 'abcB3l', 'abcB3c', '\000', 'abcB3c2'])\n".. + '==\n'.. + '=abcB3\n'.. + 'abcB3l\n'.. + 'abcB3c\n'.. + '\000\n'.. + 'abcB3c2=') + execute('%delete') + execute([=[call SetReg('C', ["\n", 'abcC3b2'], 'b')]=]) + expect( + '\n'.. + "{{{2 setreg('C', ['\000', 'abcC3b2'], 'b')\n".. + "C: type \0227; value: abcC3\000abcC3b\000abcC3l\000\000\000abcC3b2 (['abcC3', 'abcC3b', 'abcC3l', '\000', 'abcC3b2']), expr: abcC3\000abcC3b\000abcC3l\000\000\000abcC3b2 (['abcC3', 'abcC3b', 'abcC3l', '\000', 'abcC3b2'])\n".. + '==\n'.. + '=abcC3 =\n'.. + ' abcC3b\n'.. + ' abcC3l\n'.. + ' \000\n'.. + ' abcC3b2') + execute('%delete') + execute([=[call SetReg('D', ["\n", 'abcD3b50'],'b50')]=]) + expect( + '\n'.. + "{{{2 setreg('D', ['\000', 'abcD3b50'], 'b50')\n".. + "D: type \02250; value: abcD3\000abcD32\000abcD3b\000\000\000abcD3b50 (['abcD3', 'abcD32', 'abcD3b', '\000', 'abcD3b50']), expr: abcD3\000abcD32\000abcD3b\000\000\000abcD3b50 (['abcD3', 'abcD32', 'abcD3b', '\000', 'abcD3b50'])\n".. + '==\n'.. + '=abcD3 =\n'.. + ' abcD32\n'.. + ' abcD3b\n'.. + ' \000\n'.. + ' abcD3b50') + end) + + -- The tests for setting lists with NLs are split into seperate it() blocks + -- to make the expect() calls easier to write. Otherwise the null byte can + -- make trouble on a line on its own. + it('setting lists with NLs with setreg(), part 1', function() + execute('so test_eval_setup.vim') + execute([=[call SetReg('a', ['abcA4-0', "\n", "abcA4-2\n", "\nabcA4-3", "abcA4-4\nabcA4-4-2"])]=]) + expect( + '\n'.. + "{{{2 setreg('a', ['abcA4-0', '\000', 'abcA4-2\000', '\000abcA4-3', 'abcA4-4\000abcA4-4-2'])\n".. + "a: type V; value: abcA4-0\000\000\000abcA4-2\000\000\000abcA4-3\000abcA4-4\000abcA4-4-2\000 (['abcA4-0', '\000', 'abcA4-2\000', '\000abcA4-3', 'abcA4-4\000abcA4-4-2']), expr: abcA4-0\000\000\000abcA4-2\000\000\000abcA4-3\000abcA4-4\000abcA4-4-2\000 (['abcA4-0', '\000', 'abcA4-2\000', '\000abcA4-3', 'abcA4-4\000abcA4-4-2'])\n".. + '==\n'.. + 'abcA4-0\n'.. + '\000\n'.. + 'abcA4-2\000\n'.. + '\000abcA4-3\n'.. + 'abcA4-4\000abcA4-4-2\n'.. + '==') + end) + + it('setting lists with NLs with setreg(), part 2', function() + execute('so test_eval_setup.vim') + execute([=[call SetReg('b', ['abcB4c-0', "\n", "abcB4c-2\n", "\nabcB4c-3", "abcB4c-4\nabcB4c-4-2"], 'c')]=]) + expect( + '\n'.. + "{{{2 setreg('b', ['abcB4c-0', '\000', 'abcB4c-2\000', '\000abcB4c-3', 'abcB4c-4\000abcB4c-4-2'], 'c')\n".. + "b: type v; value: abcB4c-0\000\000\000abcB4c-2\000\000\000abcB4c-3\000abcB4c-4\000abcB4c-4-2 (['abcB4c-0', '\000', 'abcB4c-2\000', '\000abcB4c-3', 'abcB4c-4\000abcB4c-4-2']), expr: abcB4c-0\000\000\000abcB4c-2\000\000\000abcB4c-3\000abcB4c-4\000abcB4c-4-2 (['abcB4c-0', '\000', 'abcB4c-2\000', '\000abcB4c-3', 'abcB4c-4\000abcB4c-4-2'])\n".. + '==\n'.. + '=abcB4c-0\n'.. + '\000\n'.. + 'abcB4c-2\000\n'.. + '\000abcB4c-3\n'.. + 'abcB4c-4\000abcB4c-4-2=') + end) + + it('setting lists with NLs with setreg(), part 3', function() + execute('so test_eval_setup.vim') + execute([=[call SetReg('c', ['abcC4l-0', "\n", "abcC4l-2\n", "\nabcC4l-3", "abcC4l-4\nabcC4l-4-2"], 'l')]=]) + expect( + '\n'.. + "{{{2 setreg('c', ['abcC4l-0', '\000', 'abcC4l-2\000', '\000abcC4l-3', 'abcC4l-4\000abcC4l-4-2'], 'l')\n".. + "c: type V; value: abcC4l-0\000\000\000abcC4l-2\000\000\000abcC4l-3\000abcC4l-4\000abcC4l-4-2\000 (['abcC4l-0', '\000', 'abcC4l-2\000', '\000abcC4l-3', 'abcC4l-4\000abcC4l-4-2']), expr: abcC4l-0\000\000\000abcC4l-2\000\000\000abcC4l-3\000abcC4l-4\000abcC4l-4-2\000 (['abcC4l-0', '\000', 'abcC4l-2\000', '\000abcC4l-3', 'abcC4l-4\000abcC4l-4-2'])\n".. + '==\n'.. + 'abcC4l-0\n'.. + '\000\n'.. + 'abcC4l-2\000\n'.. + '\000abcC4l-3\n'.. + 'abcC4l-4\000abcC4l-4-2\n'.. + '==') + end) + it('setting lists with NLs with setreg(), part 4', function() + execute('so test_eval_setup.vim') + execute([=[call SetReg('d', ['abcD4b-0', "\n", "abcD4b-2\n", "\nabcD4b-3", "abcD4b-4\nabcD4b-4-2"], 'b')]=]) + expect( + '\n'.. + "{{{2 setreg('d', ['abcD4b-0', '\000', 'abcD4b-2\000', '\000abcD4b-3', 'abcD4b-4\000abcD4b-4-2'], 'b')\n".. + "d: type \02219; value: abcD4b-0\000\000\000abcD4b-2\000\000\000abcD4b-3\000abcD4b-4\000abcD4b-4-2 (['abcD4b-0', '\000', 'abcD4b-2\000', '\000abcD4b-3', 'abcD4b-4\000abcD4b-4-2']), expr: abcD4b-0\000\000\000abcD4b-2\000\000\000abcD4b-3\000abcD4b-4\000abcD4b-4-2 (['abcD4b-0', '\000', 'abcD4b-2\000', '\000abcD4b-3', 'abcD4b-4\000abcD4b-4-2'])\n".. + '==\n'.. + '=abcD4b-0 =\n'.. + ' \000\n'.. + ' abcD4b-2\000\n'.. + ' \000abcD4b-3\n'.. + ' abcD4b-4\000abcD4b-4-2') + end) + it('setting lists with NLs with setreg(), part 5', function() + execute('so test_eval_setup.vim') + execute([=[call SetReg('e', ['abcE4b10-0', "\n", "abcE4b10-2\n", "\nabcE4b10-3", "abcE4b10-4\nabcE4b10-4-2"], 'b10')]=]) + expect( + '\n'.. + "{{{2 setreg('e', ['abcE4b10-0', '\000', 'abcE4b10-2\000', '\000abcE4b10-3', 'abcE4b10-4\000abcE4b10-4-2'], 'b10')\n".. + "e: type \02210; value: abcE4b10-0\000\000\000abcE4b10-2\000\000\000abcE4b10-3\000abcE4b10-4\000abcE4b10-4-2 (['abcE4b10-0', '\000', 'abcE4b10-2\000', '\000abcE4b10-3', 'abcE4b10-4\000abcE4b10-4-2']), expr: abcE4b10-0\000\000\000abcE4b10-2\000\000\000abcE4b10-3\000abcE4b10-4\000abcE4b10-4-2 (['abcE4b10-0', '\000', 'abcE4b10-2\000', '\000abcE4b10-3', 'abcE4b10-4\000abcE4b10-4-2'])\n".. + '==\n'.. + '=abcE4b10-0=\n'.. + ' \000\n'.. + ' abcE4b10-2\000\n'.. + ' \000abcE4b10-3\n'.. + ' abcE4b10-4\000abcE4b10-4-2') + end) + + it('getreg("a",1,1) returns a valid list when "a is unset', function() + -- Precondition: "a is actually unset and "0 is nonempty + eq('', eval("getregtype('a')")) + eq('', eval("getreg('a')")) + execute("call setreg('0','text')") + + -- This used to return a NULL list + -- which setreg didn't handle + execute("let x = getreg('a',1,1)") + execute("call setreg('0',x)") + + -- nvim didn't crash and "0 was emptied + eq(2, eval("1+1")) + eq({}, eval("getreg('0',1,1)")) + + -- x is a mutable list + execute("let y = x") + eq({}, eval("y")) + execute("call add(x, 'item')") + eq({'item'}, eval("y")) + end) + + it('search and expressions', function() + execute('so test_eval_setup.vim') + execute([=[call SetReg('/', ['abc/'])]=]) + execute([=[call SetReg('/', ["abc/\n"])]=]) + execute([=[call SetReg('=', ['"abc/"'])]=]) + execute([=[call SetReg('=', ["\"abc/\n\""])]=]) + expect([[ + + {{{2 setreg('/', ['abc/']) + /: type v; value: abc/ (['abc/']), expr: abc/ (['abc/']) + == + =abc/= + {{{2 setreg('/', ['abc/]]..'\000'..[[']) + /: type v; value: abc/]].."\000 (['abc/\000']), expr: abc/\000 (['abc/\000"..[[']) + == + =abc/]]..'\000'..[[= + {{{2 setreg('=', ['"abc/"']) + =: type v; value: abc/ (['abc/']), expr: "abc/" (['"abc/"']) + {{{2 setreg('=', ['"abc/]]..'\000'..[["']) + =: type v; value: abc/]].."\000 (['abc/\000"..[[']), expr: "abc/]]..'\000'..[[" (['"abc/]]..'\000'..[["'])]]) + end) + + if has_clipboard() then + it('system clipboard', function() + insert([[ + Some first line (this text was at the top of the old test_eval.in). + + Note: system clipboard is saved, changed and restored. + + clipboard contents + something else]]) + execute('so test_eval_setup.vim') + -- Save and restore system clipboard. + execute("let _clipreg = ['*', getreg('*'), getregtype('*')]") + execute('let _clipopt = &cb') + execute("let &cb='unnamed'") + execute('5y') + execute('AR *') + execute('tabdo :windo :echo "hi"') + execute('6y') + execute('AR *') + execute('let &cb=_clipopt') + execute("call call('setreg', _clipreg)") + expect([[ + Some first line (this text was at the top of the old test_eval.in). + + Note: system clipboard is saved, changed and restored. + + clipboard contents + something else + *: type V; value: clipboard contents]]..'\00'..[[ (['clipboard contents']), expr: clipboard contents]]..'\00'..[[ (['clipboard contents']) + *: type V; value: something else]]..'\00'..[[ (['something else']), expr: something else]]..'\00'..[[ (['something else'])]]) + end) + else + pending('system clipboard not available', function() end) + end + + it('errors', function() + source([[ + fun ErrExe(str) + call append('$', 'Executing '.a:str) + try + execute a:str + catch + $put =v:exception + endtry + endfun]]) + execute([[call ErrExe('call setreg()')]]) + execute([[call ErrExe('call setreg(1)')]]) + execute([[call ErrExe('call setreg(1, 2, 3, 4)')]]) + execute([=[call ErrExe('call setreg([], 2)')]=]) + execute([[call ErrExe('call setreg(1, {})')]]) + execute([=[call ErrExe('call setreg(1, 2, [])')]=]) + execute([=[call ErrExe('call setreg("/", ["1", "2"])')]=]) + execute([=[call ErrExe('call setreg("=", ["1", "2"])')]=]) + execute([=[call ErrExe('call setreg(1, ["", "", [], ""])')]=]) + expect([[ + + Executing call setreg() + Vim(call):E119: Not enough arguments for function: setreg + Executing call setreg(1) + Vim(call):E119: Not enough arguments for function: setreg + Executing call setreg(1, 2, 3, 4) + Vim(call):E118: Too many arguments for function: setreg + Executing call setreg([], 2) + Vim(call):E730: using List as a String + Executing call setreg(1, {}) + Vim(call):E731: using Dictionary as a String + Executing call setreg(1, 2, []) + Vim(call):E730: using List as a String + Executing call setreg("/", ["1", "2"]) + Vim(call):E883: search pattern and expression register may not contain two or more lines + Executing call setreg("=", ["1", "2"]) + Vim(call):E883: search pattern and expression register may not contain two or more lines + Executing call setreg(1, ["", "", [], ""]) + Vim(call):E730: using List as a String]]) + end) + + it('function name not starting with a capital', function() + execute('try') + execute(' func! g:test()') + execute(' echo "test"') + execute(' endfunc') + execute('catch') + execute(' let tmp = v:exception') + execute('endtry') + eq('Vim(function):E128: Function name must start with a capital or "s:": g:test()', eval('tmp')) + end) + + it('Function name followed by #', function() + execute('try') + execute(' func! test2() "#') + execute(' echo "test2"') + execute(' endfunc') + execute('catch') + execute(' let tmp = v:exception') + execute('endtry') + eq('Vim(function):E128: Function name must start with a capital or "s:": test2() "#', eval('tmp')) + end) + + it('function name includes a colon', function() + execute('try') + execute(' func! b:test()') + execute(' echo "test"') + execute(' endfunc') + execute('catch') + execute(' let tmp = v:exception') + execute('endtry') + eq('Vim(function):E128: Function name must start with a capital or "s:": b:test()', eval('tmp')) + end) + + it('function name starting with/without "g:", buffer-local funcref', function() + execute('function! g:Foo(n)') + execute(" $put ='called Foo(' . a:n . ')'") + execute('endfunction') + execute("let b:my_func = function('Foo')") + execute('call b:my_func(1)') + execute('echo g:Foo(2)') + execute('echo Foo(3)') + expect([[ + + called Foo(1) + called Foo(2) + called Foo(3)]]) + end) + + it('script-local function used in Funcref must exist', function() + source([[ + " Vim script used in test_eval.in. Needed for script-local function. + + func! s:Testje() + return "foo" + endfunc + + let Bar = function('s:Testje') + + $put ='s:Testje exists: ' . exists('s:Testje') + $put ='func s:Testje exists: ' . exists('*s:Testje') + $put ='Bar exists: ' . exists('Bar') + $put ='func Bar exists: ' . exists('*Bar') + ]]) + expect([[ + + s:Testje exists: 0 + func s:Testje exists: 1 + Bar exists: 1 + func Bar exists: 1]]) + end) + + it("using $ instead of '$' must give an error", function() + execute('try') + execute(" call append($, 'foobar')") + execute('catch') + execute(' let tmp = v:exception') + execute('endtry') + eq('Vim(call):E116: Invalid arguments for function append', eval('tmp')) + end) + + it('getcurpos/setpos', function() + insert([[ + 012345678 + 012345678 + + start:]]) + execute('/^012345678') + feed('6l') + execute('let sp = getcurpos()') + feed('0') + execute("call setpos('.', sp)") + feed('jyl') + execute('$put') + expect([[ + 012345678 + 012345678 + + start: + 6]]) + end) + + it('substring and variable name', function() + execute("let str = 'abcdef'") + execute('let n = 3') + eq('def', eval('str[n:]')) + eq('abcd', eval('str[:n]')) + eq('d', eval('str[n:n]')) + execute('unlet n') + execute('let nn = 3') + eq('def', eval('str[nn:]')) + eq('abcd', eval('str[:nn]')) + eq('d', eval('str[nn:nn]')) + execute('unlet nn') + execute('let b:nn = 4') + eq('ef', eval('str[b:nn:]')) + eq('abcde', eval('str[:b:nn]')) + eq('e', eval('str[b:nn:b:nn]')) + end) +end) diff --git a/test/functional/legacy/fixeol_spec.lua b/test/functional/legacy/fixeol_spec.lua new file mode 100644 index 0000000000..2d1824c8cd --- /dev/null +++ b/test/functional/legacy/fixeol_spec.lua @@ -0,0 +1,72 @@ +-- Tests for 'fixeol' + +local helpers = require('test.functional.helpers') +local feed = helpers.feed +local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect + +describe('fixeol', function() + local function rmtestfiles() + os.remove('test.out') + os.remove('XXEol') + os.remove('XXNoEol') + os.remove('XXTestEol') + os.remove('XXTestNoEol') + end + setup(function() + clear() + rmtestfiles() + end) + teardown(function() + rmtestfiles() + end) + + it('is working', function() + -- First write two test files – with and without trailing EOL. + -- Use Unix fileformat for consistency. + execute('set ff=unix') + execute('enew!') + feed('awith eol<esc>:w! XXEol<cr>') + execute('enew!') + execute('set noeol nofixeol') + feed('awithout eol<esc>:w! XXNoEol<cr>') + execute('set eol fixeol') + execute('bwipe XXEol XXNoEol') + + -- Try editing files with 'fixeol' disabled. + execute('e! XXEol') + feed('ostays eol<esc>:set nofixeol<cr>') + execute('w! XXTestEol') + execute('e! XXNoEol') + feed('ostays without<esc>:set nofixeol<cr>') + execute('w! XXTestNoEol') + execute('bwipe XXEol XXNoEol XXTestEol XXTestNoEol') + execute('set fixeol') + + -- Append "END" to each file so that we can see what the last written char was. + feed('ggdGaEND<esc>:w >>XXEol<cr>') + execute('w >>XXNoEol') + execute('w >>XXTestEol') + execute('w >>XXTestNoEol') + + -- Concatenate the results. + execute('e! test.out') + feed('a0<esc>:$r XXEol<cr>') + execute('$r XXNoEol') + feed('Go1<esc>:$r XXTestEol<cr>') + execute('$r XXTestNoEol') + execute('w') + + -- Assert buffer contents. + expect([=[ + 0 + with eol + END + without eolEND + 1 + with eol + stays eol + END + without eol + stays withoutEND]=]) + end) +end) diff --git a/test/functional/legacy/fnamemodify_spec.lua b/test/functional/legacy/fnamemodify_spec.lua new file mode 100644 index 0000000000..2a32aea127 --- /dev/null +++ b/test/functional/legacy/fnamemodify_spec.lua @@ -0,0 +1,75 @@ +-- Test filename modifiers. + +local helpers = require('test.functional.helpers') +local clear, source = helpers.clear, helpers.source +local call, eq, nvim = helpers.call, helpers.eq, helpers.meths + +local function expected_empty() + eq({}, nvim.get_vvar('errors')) +end + +describe('filename modifiers', function() + before_each(function() + clear() + + source([=[ + func Test_fnamemodify() + let tmpdir = resolve('/tmp') + execute 'cd '. tmpdir + set shell=sh + set shellslash + let $HOME=fnamemodify('.', ':p:h:h:h') + call assert_equal('/', fnamemodify('.', ':p')[-1:]) + call assert_equal('p', fnamemodify('.', ':p:h')[-1:]) + call assert_equal('t', fnamemodify('test.out', ':p')[-1:]) + call assert_equal('test.out', fnamemodify('test.out', ':.')) + call assert_equal('../testdir/a', fnamemodify('../testdir/a', ':.')) + call assert_equal('test.out', fnamemodify('test.out', ':~')) + call assert_equal('../testdir/a', fnamemodify('../testdir/a', ':~')) + call assert_equal('a', fnamemodify('../testdir/a', ':t')) + call assert_equal('', fnamemodify('.', ':p:t')) + call assert_equal('test.out', fnamemodify('test.out', ':p:t')) + call assert_equal('out', fnamemodify('test.out', ':p:e')) + call assert_equal('out', fnamemodify('test.out', ':p:t:e')) + call assert_equal('abc.fb2.tar', fnamemodify('abc.fb2.tar.gz', ':r')) + call assert_equal('abc.fb2', fnamemodify('abc.fb2.tar.gz', ':r:r')) + call assert_equal('abc', fnamemodify('abc.fb2.tar.gz', ':r:r:r')) + call assert_equal(tmpdir .'/abc.fb2', substitute(fnamemodify('abc.fb2.tar.gz', ':p:r:r'), '.*\(nvim/testdir/.*\)', '\1', '')) + call assert_equal('gz', fnamemodify('abc.fb2.tar.gz', ':e')) + call assert_equal('tar.gz', fnamemodify('abc.fb2.tar.gz', ':e:e')) + call assert_equal('fb2.tar.gz', fnamemodify('abc.fb2.tar.gz', ':e:e:e')) + call assert_equal('fb2.tar.gz', fnamemodify('abc.fb2.tar.gz', ':e:e:e:e')) + call assert_equal('tar', fnamemodify('abc.fb2.tar.gz', ':e:e:r')) + call assert_equal('''abc def''', fnamemodify('abc def', ':S')) + call assert_equal('''abc" "def''', fnamemodify('abc" "def', ':S')) + call assert_equal('''abc"%"def''', fnamemodify('abc"%"def', ':S')) + call assert_equal('''abc''\'''' ''\''''def''', fnamemodify('abc'' ''def', ':S')) + call assert_equal('''abc''\''''%''\''''def''', fnamemodify('abc''%''def', ':S')) + new foo.txt + call assert_equal(expand('%:r:S'), shellescape(expand('%:r'))) + call assert_equal('foo,''foo'',foo.txt', join([expand('%:r'), expand('%:r:S'), expand('%')], ',')) + quit + + call assert_equal("'abc\ndef'", fnamemodify("abc\ndef", ':S')) + set shell=tcsh + call assert_equal("'abc\\\ndef'", fnamemodify("abc\ndef", ':S')) + endfunc + + func Test_expand() + new + call assert_equal("", expand('%:S')) + quit + endfunc + ]=]) + end) + + it('is working', function() + call('Test_fnamemodify') + expected_empty() + end) + + it('works for :S in an unnamed buffer', function() + call('Test_expand') + expected_empty() + end) +end) diff --git a/test/functional/legacy/function_sort_spec.lua b/test/functional/legacy/function_sort_spec.lua new file mode 100644 index 0000000000..9083911021 --- /dev/null +++ b/test/functional/legacy/function_sort_spec.lua @@ -0,0 +1,29 @@ +local helpers = require('test.functional.helpers') +local clear = helpers.clear +local eq = helpers.eq +local eval = helpers.eval + +describe('sort', function() + before_each(clear) + + it('numbers compared as strings', function() + eq({1, 2, 3}, eval('sort([3, 2, 1])')) + eq({13, 28, 3}, eval('sort([3, 28, 13])')) + end) + + it('numbers compared as numeric', function() + eq({1, 2, 3}, eval("sort([3, 2, 1], 'n')")) + eq({3, 13, 28}, eval("sort([3, 28, 13], 'n')")) + -- Strings are not sorted. + eq({'13', '28', '3'}, eval("sort(['13', '28', '3'], 'n')")) + end) + + it('numbers compared as numbers', function() + eq({3, 13, 28}, eval("sort([13, 28, 3], 'N')")) + eq({'3', '13', '28'}, eval("sort(['13', '28', '3'], 'N')")) + end) + + it('numbers compared as float', function() + eq({0.28, 3, 13.5}, eval("sort([13.5, 0.28, 3], 'f')")) + end) +end) diff --git a/test/functional/legacy/glob2regpat_spec.lua b/test/functional/legacy/glob2regpat_spec.lua new file mode 100644 index 0000000000..0492143616 --- /dev/null +++ b/test/functional/legacy/glob2regpat_spec.lua @@ -0,0 +1,22 @@ +-- Tests for signs + +local helpers = require('test.functional.helpers') +local clear, execute = helpers.clear, helpers.execute +local eq, neq, eval = helpers.eq, helpers.neq, helpers.eval + +describe('glob2regpat()', function() + before_each(clear) + + it('handles invalid input', function() + execute('call glob2regpat(1.33)') + helpers.feed('<cr>') + neq(nil, string.find(eval('v:errmsg'), '^E806:')) + end) + it('returns ^$ for empty input', function() + eq('^$', eval("glob2regpat('')")) + end) + it('handles valid input', function() + eq('^foo\\.', eval("glob2regpat('foo.*')")) + eq('\\.vim$', eval("glob2regpat('*.vim')")) + end) +end) diff --git a/test/functional/legacy/increment_spec.lua b/test/functional/legacy/increment_spec.lua new file mode 100644 index 0000000000..4aa24c0d53 --- /dev/null +++ b/test/functional/legacy/increment_spec.lua @@ -0,0 +1,748 @@ +-- Tests for using Ctrl-A/Ctrl-X on visual selections + +local helpers = require('test.functional.helpers') +local source, execute = helpers.source, helpers.execute +local call, clear = helpers.call, helpers.clear +local eq, nvim = helpers.eq, helpers.meths + +describe('Ctrl-A/Ctrl-X on visual selections', function() + + before_each(function() + clear() + source([=[ + " 1) Ctrl-A on visually selected number + " Text: + " foobar-10 + " Expected: + " 1) Ctrl-A on start of line: + " foobar-9 + " 2) Ctrl-A on visually selected "-10": + " foobar-9 + " 3) Ctrl-A on visually selected "10": + " foobar-11 + " 4) Ctrl-X on visually selected "-10" + " foobar-11 + " 5) Ctrl-X on visually selected "10" + " foobar-9 + func Test_visual_increment_01() + call setline(1, repeat(["foobaar-10"], 5)) + + call cursor(1, 1) + exec "norm! \<C-A>" + call assert_equal("foobaar-9", getline('.')) + call assert_equal([0, 1, 9, 0], getpos('.')) + + call cursor(2, 1) + exec "norm! f-v$\<C-A>" + call assert_equal("foobaar-9", getline('.')) + call assert_equal([0, 2, 8, 0], getpos('.')) + + call cursor(3, 1) + exec "norm! f1v$\<C-A>" + call assert_equal("foobaar-11", getline('.')) + call assert_equal([0, 3, 9, 0], getpos('.')) + + call cursor(4, 1) + exec "norm! f-v$\<C-X>" + call assert_equal("foobaar-11", getline('.')) + call assert_equal([0, 4, 8, 0], getpos('.')) + + call cursor(5, 1) + exec "norm! f1v$\<C-X>" + call assert_equal("foobaar-9", getline('.')) + call assert_equal([0, 5, 9, 0], getpos('.')) + endfunc + + " 2) Ctrl-A on visually selected lines + " Text: + " 10 + " 20 + " 30 + " 40 + " + " Expected: + " 1) Ctrl-A on visually selected lines: + " 11 + " 21 + " 31 + " 41 + " + " 2) Ctrl-X on visually selected lines: + " 9 + " 19 + " 29 + " 39 + func Test_visual_increment_02() + call setline(1, ["10", "20", "30", "40"]) + exec "norm! GV3k$\<C-A>" + call assert_equal(["11", "21", "31", "41"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + + call setline(1, ["10", "20", "30", "40"]) + exec "norm! GV3k$\<C-X>" + call assert_equal(["9", "19", "29", "39"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 3) g Ctrl-A on visually selected lines, with non-numbers in between + " Text: + " 10 + " + " 20 + " + " 30 + " + " 40 + " + " Expected: + " 1) 2 g Ctrl-A on visually selected lines: + " 12 + " + " 24 + " + " 36 + " + " 48 + " 2) 2 g Ctrl-X on visually selected lines + " 8 + " + " 16 + " + " 24 + " + " 32 + func Test_visual_increment_03() + call setline(1, ["10", "", "20", "", "30", "", "40"]) + exec "norm! GV6k2g\<C-A>" + call assert_equal(["12", "", "24", "", "36", "", "48"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + + call setline(1, ["10", "", "20", "", "30", "", "40"]) + exec "norm! GV6k2g\<C-X>" + call assert_equal(["8", "", "16", "", "24", "", "32"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 4) Ctrl-A on non-number + " Text: + " foobar-10 + " Expected: + " 1) visually select foobar: + " foobar-10 + func Test_visual_increment_04() + call setline(1, ["foobar-10"]) + exec "norm! vf-\<C-A>" + call assert_equal(["foobar-10"], getline(1, '$')) + " NOTE: I think this is correct behavior... + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 5) g<Ctrl-A> on letter + " Test: + " a + " a + " a + " a + " Expected: + " 1) g Ctrl-A on visually selected lines + " b + " c + " d + " e + func Test_visual_increment_05() + set nrformats+=alpha + call setline(1, repeat(["a"], 4)) + exec "norm! GV3kg\<C-A>" + call assert_equal(["b", "c", "d", "e"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 6) g<Ctrl-A> on letter + " Test: + " z + " z + " z + " z + " Expected: + " 1) g Ctrl-X on visually selected lines + " y + " x + " w + " v + func Test_visual_increment_06() + set nrformats+=alpha + call setline(1, repeat(["z"], 4)) + exec "norm! GV3kg\<C-X>" + call assert_equal(["y", "x", "w", "v"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 7) <Ctrl-A> on letter + " Test: + " 2 + " 1 + " 0 + " -1 + " -2 + " + " Expected: + " 1) Ctrl-A on visually selected lines + " 3 + " 2 + " 1 + " 0 + " -1 + " + " 2) Ctrl-X on visually selected lines + " 1 + " 0 + " -1 + " -2 + " -3 + func Test_visual_increment_07() + call setline(1, ["2", "1", "0", "-1", "-2"]) + exec "norm! GV4k\<C-A>" + call assert_equal(["3", "2", "1", "0", "-1"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + + call setline(1, ["2", "1", "0", "-1", "-2"]) + exec "norm! GV4k\<C-X>" + call assert_equal(["1", "0", "-1", "-2", "-3"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 8) Block increment on 0x9 + " Text: + " 0x9 + " 0x9 + " Expected: + " 1) Ctrl-A on visually block selected region (cursor at beginning): + " 0xa + " 0xa + " 2) Ctrl-A on visually block selected region (cursor at end) + " 0xa + " 0xa + func Test_visual_increment_08() + call setline(1, repeat(["0x9"], 2)) + exec "norm! \<C-V>j$\<C-A>" + call assert_equal(["0xa", "0xa"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + + call setline(1, repeat(["0x9"], 2)) + exec "norm! gg$\<C-V>+\<C-A>" + call assert_equal(["0xa", "0xa"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 9) Increment and redo + " Text: + " 2 + " 2 + " + " 3 + " 3 + " + " Expected: + " 1) 2 Ctrl-A on first 2 visually selected lines + " 4 + " 4 + " 2) redo (.) on 3 + " 5 + " 5 + func Test_visual_increment_09() + call setline(1, ["2", "2", "", "3", "3", ""]) + exec "norm! ggVj2\<C-A>" + call assert_equal(["4", "4", "", "3", "3", ""], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + + exec "norm! 3j." + call assert_equal(["4", "4", "", "5", "5", ""], getline(1, '$')) + call assert_equal([0, 4, 1, 0], getpos('.')) + endfunc + + " 10) sequentially decrement 1 + " Text: + " 1 + " 1 + " 1 + " 1 + " Expected: + " 1) g Ctrl-X on visually selected lines + " 0 + " -1 + " -2 + " -3 + func Test_visual_increment_10() + call setline(1, repeat(["1"], 4)) + exec "norm! GV3kg\<C-X>" + call assert_equal(["0", "-1", "-2", "-3"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 11) visually block selected indented lines + " Text: + " 1 + " 1 + " 1 + " 1 + " Expexted: + " 1) g Ctrl-A on block selected indented lines + " 2 + " 1 + " 3 + " 4 + func Test_visual_increment_11() + call setline(1, [" 1", "1", " 1", " 1"]) + exec "norm! f1\<C-V>3jg\<C-A>" + call assert_equal([" 2", "1", " 3", " 4"], getline(1, '$')) + call assert_equal([0, 1, 5, 0], getpos('.')) + endfunc + + " 12) visually selected several columns + " Text: + " 0 0 + " 0 0 + " 0 0 + " Expected: + " 1) 'v' select last zero and first zeroes + " 0 1 + " 1 0 + " 1 0 + func Test_visual_increment_12() + call setline(1, repeat(["0 0"], 3)) + exec "norm! $v++\<C-A>" + call assert_equal(["0 1", "1 0", "1 0"], getline(1, '$')) + call assert_equal([0, 1, 3, 0], getpos('.')) + endfunc + + " 13) visually selected part of columns + " Text: + " max: 100px + " max: 200px + " max: 300px + " max: 400px + " Expected: + " 1) 'v' on first two numbers Ctrl-A + " max: 110px + " max: 220px + " max: 330px + " max: 400px + " 2) 'v' on first two numbers Ctrl-X + " max: 90px + " max: 190px + " max: 290px + " max: 400px + func Test_visual_increment_13() + call setline(1, ["max: 100px", "max: 200px", "max: 300px", "max: 400px"]) + exec "norm! f1\<C-V>l2j\<C-A>" + call assert_equal(["max: 110px", "max: 210px", "max: 310px", "max: 400px"], getline(1, '$')) + call assert_equal([0, 1, 6, 0], getpos('.')) + + call setline(1, ["max: 100px", "max: 200px", "max: 300px", "max: 400px"]) + exec "norm! ggf1\<C-V>l2j\<C-X>" + call assert_equal(["max: 90px", "max: 190px", "max: 290px", "max: 400px"], getline(1, '$')) + call assert_equal([0, 1, 6, 0], getpos('.')) + endfunc + + " 14) redo in block mode + " Text: + " 1 1 + " 1 1 + " Expected: + " 1) Ctrl-a on first column, redo on second column + " 2 2 + " 2 2 + func Test_visual_increment_14() + call setline(1, repeat(["1 1"], 2)) + exec "norm! G\<C-V>k\<C-A>w." + call assert_equal(["2 2", "2 2"], getline(1, '$')) + call assert_equal([0, 1, 3, 0], getpos('.')) + endfunc + + " 15) block select single numbers + " Text: + " 101 + " Expected: + " 1) Ctrl-a on visually selected zero + " 111 + func Test_visual_increment_15() + call setline(1, ["101"]) + exec "norm! lv\<C-A>" + call assert_equal(["111"], getline(1, '$')) + call assert_equal([0, 1, 2, 0], getpos('.')) + endfunc + + " 16) increment right aligned numbers + " Text: + " 1 + " 19 + " 119 + " Expected: + " 1) Ctrl-a on line selected region + " 2 + " 20 + " 120 + func Test_visual_increment_16() + call setline(1, [" 1", " 19", " 119"]) + exec "norm! VG\<C-A>" + call assert_equal([" 2", " 20", " 120"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 17) block-wise increment and redo + " Text: + " 100 + " 1 + " + " 100 + " 1 + " + " Expected: + " 1) Ctrl-V j $ on first block, afterwards '.' on second + " 101 + " 2 + " + " 101 + " 2 + func Test_visual_increment_17() + call setline(1, [" 100", " 1", "", " 100", " 1"]) + exec "norm! \<C-V>j$\<C-A>2j." + call assert_equal([" 101", " 2", "", " 101", " 1"], getline(1, '$')) + call assert_equal([0, 3, 1, 0], getpos('.')) + endfunc + + " 18) repeat of g<Ctrl-a> + " Text: + " 0 + " 0 + " 0 + " 0 + " + " Expected: + " 1) V 4j g<ctrl-a>, repeat twice afterwards with . + " 3 + " 6 + " 9 + " 12 + func Test_visual_increment_18() + call setline(1, repeat(["0"], 4)) + exec "norm! GV3kg\<C-A>" + exec "norm! .." + call assert_equal(["3", "6", "9", "12"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 19) increment on number with nrformat including alpha + " Text: + " 1 + " 1a + " + " Expected: + " 1) <Ctrl-V>j$ <ctrl-a> + " 2 + " 2a + func Test_visual_increment_19() + set nrformats+=alpha + call setline(1, ["1", "1a"]) + exec "norm! \<C-V>G$\<C-A>" + call assert_equal(["2", "2a"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 20) increment a single letter + " Text: + " a + " + " Expected: + " 1) <Ctrl-a> and cursor is on a + " b + func Test_visual_increment_20() + set nrformats+=alpha + call setline(1, ["a"]) + exec "norm! \<C-A>" + call assert_equal(["b"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 21) block-wise increment on part of hexadecimal + " Text: + " 0x123456 + " + " Expected: + " 1) Ctrl-V f3 <ctrl-a> + " 0x124456 + func Test_visual_increment_21() + call setline(1, ["0x123456"]) + exec "norm! \<C-V>f3\<C-A>" + call assert_equal(["0x124456"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 22) Block increment on 0b0 + " Text: + " 0b1 + " 0b1 + " Expected: + " 1) Ctrl-A on visually block selected region (cursor at beginning): + " 0b10 + " 0b10 + " 2) Ctrl-A on visually block selected region (cursor at end) + " 0b10 + " 0b10 + func Test_visual_increment_22() + call setline(1, repeat(["0b1"], 2)) + exec "norm! \<C-V>j$\<C-A>" + call assert_equal(repeat(["0b10"], 2), getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + + call setline(1, repeat(["0b1"], 2)) + exec "norm! $\<C-V>+\<C-A>" + call assert_equal(repeat(["0b10"], 2), getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 23) block-wise increment on part of binary + " Text: + " 0b1001 + " + " Expected: + " 1) Ctrl-V 5l <ctrl-a> + " 0b1011 + func Test_visual_increment_23() + call setline(1, ["0b1001"]) + exec "norm! \<C-V>4l\<C-A>" + call assert_equal(["0b1011"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 24) increment hexadecimal + " Text: + " 0x0b1001 + " + " Expected: + " 1) <ctrl-a> + " 0x0b1002 + func Test_visual_increment_24() + call setline(1, ["0x0b1001"]) + exec "norm! \<C-V>$\<C-A>" + call assert_equal(["0x0b1002"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 25) increment binary with nrformats including alpha + " Text: + " 0b1001a + " + " Expected: + " 1) <ctrl-a> + " 0b1010a + func Test_visual_increment_25() + set nrformats+=alpha + call setline(1, ["0b1001a"]) + exec "norm! \<C-V>$\<C-A>" + call assert_equal(["0b1010a"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 26) increment binary with 32 bits + " Text: + " 0b11111111111111111111111111111110 + " + " Expected: + " 1) <ctrl-a> + " 0b11111111111111111111111111111111 + func Test_visual_increment_26() + set nrformats+=alpha + call setline(1, ["0b11111111111111111111111111111110"]) + exec "norm! \<C-V>$\<C-A>" + call assert_equal(["0b11111111111111111111111111111111"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + set nrformats-=alpha + endfunc + + " 27) increment with 'rightreft', if supported + func Test_visual_increment_27() + if exists('+rightleft') + set rightleft + call setline(1, ["1234 56"]) + + exec "norm! $\<C-A>" + call assert_equal(["1234 57"], getline(1, '$')) + call assert_equal([0, 1, 7, 0], getpos('.')) + + exec "norm! \<C-A>" + call assert_equal(["1234 58"], getline(1, '$')) + call assert_equal([0, 1, 7, 0], getpos('.')) + set norightleft + endif + endfunc + + " Tab code and linewise-visual inc/dec + func Test_visual_increment_28() + call setline(1, ["x\<TAB>10", "\<TAB>-1"]) + exec "norm! Vj\<C-A>" + call assert_equal(["x\<TAB>11", "\<TAB>0"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + + call setline(1, ["x\<TAB>10", "\<TAB>-1"]) + exec "norm! ggVj\<C-X>" + call assert_equal(["x\<TAB>9", "\<TAB>-2"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " Tab code and linewise-visual inc/dec with 'nrformats'+=alpha + func Test_visual_increment_29() + set nrformats+=alpha + call setline(1, ["x\<TAB>10", "\<TAB>-1"]) + exec "norm! Vj\<C-A>" + call assert_equal(["y\<TAB>10", "\<TAB>0"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + + call setline(1, ["x\<TAB>10", "\<TAB>-1"]) + exec "norm! ggVj\<C-X>" + call assert_equal(["w\<TAB>10", "\<TAB>-2"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " Tab code and character-visual inc/dec + func Test_visual_increment_30() + call setline(1, ["x\<TAB>10", "\<TAB>-1"]) + exec "norm! f1vjf1\<C-A>" + call assert_equal(["x\<TAB>11", "\<TAB>0"], getline(1, '$')) + call assert_equal([0, 1, 3, 0], getpos('.')) + + call setline(1, ["x\<TAB>10", "\<TAB>-1"]) + exec "norm! ggf1vjf1\<C-X>" + call assert_equal(["x\<TAB>9", "\<TAB>-2"], getline(1, '$')) + call assert_equal([0, 1, 3, 0], getpos('.')) + endfunc + + " Tab code and blockwise-visual inc/dec + func Test_visual_increment_31() + call setline(1, ["x\<TAB>10", "\<TAB>-1"]) + exec "norm! f1\<C-V>jl\<C-A>" + call assert_equal(["x\<TAB>11", "\<TAB>0"], getline(1, '$')) + call assert_equal([0, 1, 3, 0], getpos('.')) + + call setline(1, ["x\<TAB>10", "\<TAB>-1"]) + exec "norm! ggf1\<C-V>jl\<C-X>" + call assert_equal(["x\<TAB>9", "\<TAB>-2"], getline(1, '$')) + call assert_equal([0, 1, 3, 0], getpos('.')) + endfunc + + " Tab code and blockwise-visual decrement with 'linebreak' and 'showbreak' + func Test_visual_increment_32() + 28vnew dummy_31 + set linebreak showbreak=+ + call setline(1, ["x\<TAB>\<TAB>\<TAB>10", "\<TAB>\<TAB>\<TAB>\<TAB>-1"]) + exec "norm! ggf0\<C-V>jg_\<C-X>" + call assert_equal(["x\<TAB>\<TAB>\<TAB>1-1", "\<TAB>\<TAB>\<TAB>\<TAB>-2"], getline(1, '$')) + call assert_equal([0, 1, 6, 0], getpos('.')) + bwipe! + endfunc + + " Tab code and blockwise-visual increment with $ + func Test_visual_increment_33() + call setline(1, ["\<TAB>123", "456"]) + exec "norm! gg0\<C-V>j$\<C-A>" + call assert_equal(["\<TAB>124", "457"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " Tab code and blockwise-visual increment and redo + func Test_visual_increment_34() + call setline(1, ["\<TAB>123", " 456789"]) + exec "norm! gg0\<C-V>j\<C-A>" + call assert_equal(["\<TAB>123", " 457789"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + + exec "norm! .." + call assert_equal(["\<TAB>123", " 459789"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " Tab code, spaces and character-visual increment and redo + func Test_visual_increment_35() + call setline(1, ["\<TAB>123", " 123", "\<TAB>123", "\<TAB>123"]) + exec "norm! ggvjf3\<C-A>..." + call assert_equal(["\<TAB>127", " 127", "\<TAB>123", "\<TAB>123"], getline(1, '$')) + call assert_equal([0, 1, 2, 0], getpos('.')) + endfunc + + " Tab code, spaces and blockwise-visual increment and redo + func Test_visual_increment_36() + call setline(1, [" 123", "\<TAB>456789"]) + exec "norm! G0\<C-V>kl\<C-A>" + call assert_equal([" 123", "\<TAB>556789"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + + exec "norm! ..." + call assert_equal([" 123", "\<TAB>856789"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " block-wise increment and dot-repeat + " Text: + " 1 23 + " 4 56 + " + " Expected: + " 1) f2 Ctrl-V jl <ctrl-a>, repeat twice afterwards with . + " 1 26 + " 4 59 + " + " Try with and without indent. + func Test_visual_increment_37() + call setline(1, [" 1 23", " 4 56"]) + exec "norm! ggf2\<C-V>jl\<C-A>.." + call assert_equal([" 1 26", " 4 59"], getline(1, 2)) + + call setline(1, ["1 23", "4 56"]) + exec "norm! ggf2\<C-V>jl\<C-A>.." + call assert_equal(["1 26", "4 59"], getline(1, 2)) + endfunc + + " Check redo after the normal mode increment + func Test_visual_increment_38() + exec "norm! i10\<ESC>5\<C-A>." + call assert_equal(["20"], getline(1, '$')) + call assert_equal([0, 1, 2, 0], getpos('.')) + endfunc + + " Test what patch 7.3.414 fixed. Ctrl-A on "000" drops the leading zeros. + func Test_normal_increment_01() + call setline(1, "000") + exec "norm! gg0\<C-A>" + call assert_equal("001", getline(1)) + + call setline(1, "000") + exec "norm! gg$\<C-A>" + call assert_equal("001", getline(1)) + + call setline(1, "001") + exec "norm! gg0\<C-A>" + call assert_equal("002", getline(1)) + + call setline(1, "001") + exec "norm! gg$\<C-A>" + call assert_equal("002", getline(1)) + endfunc + ]=]) + end) + + for i = 1, 38 do + local id = string.format('%02d', i) + + it('works on Test ' .. id, function() + execute('set nrformats&vi') -- &vi makes Vim compatible + call('Test_visual_increment_' .. id) + eq({}, nvim.get_vvar('errors')) + end) + end + + it('does not drop leading zeroes', function() + execute('set nrformats&vi') -- &vi makes Vim compatible + call('Test_normal_increment_01') + eq({}, nvim.get_vvar('errors')) + end) +end) diff --git a/test/functional/legacy/lispwords_spec.lua b/test/functional/legacy/lispwords_spec.lua new file mode 100644 index 0000000000..48df4de55e --- /dev/null +++ b/test/functional/legacy/lispwords_spec.lua @@ -0,0 +1,25 @@ +local helpers = require('test.functional.helpers') +local clear = helpers.clear +local eq = helpers.eq +local eval = helpers.eval +local execute = helpers.execute +local source = helpers.source + +describe('lispwords', function() + before_each(clear) + + it('should be set global-local',function() + source([[ + setglobal lispwords=foo,bar,baz + setlocal lispwords-=foo + setlocal lispwords+=quux]]) + eq('foo,bar,baz', eval('&g:lispwords')) + eq('bar,baz,quux', eval('&l:lispwords')) + eq('bar,baz,quux', eval('&lispwords')) + + execute('setlocal lispwords<') + eq('foo,bar,baz', eval('&g:lispwords')) + eq('foo,bar,baz', eval('&l:lispwords')) + eq('foo,bar,baz', eval('&lispwords')) + end) +end) diff --git a/test/functional/legacy/listlbr_utf8_spec.lua b/test/functional/legacy/listlbr_utf8_spec.lua index 15d12ac4af..df0e817533 100644 --- a/test/functional/legacy/listlbr_utf8_spec.lua +++ b/test/functional/legacy/listlbr_utf8_spec.lua @@ -1,8 +1,9 @@ -- Test for linebreak and list option in utf-8 mode local helpers = require('test.functional.helpers') -local feed, insert, source = helpers.feed, helpers.insert, helpers.source -local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect +local source = helpers.source +local feed = helpers.feed +local clear, expect = helpers.clear, helpers.expect describe('linebreak', function() setup(clear) @@ -32,16 +33,19 @@ describe('linebreak', function() $put =g:line wincmd p endfu + " let g:test ="Test 1: set linebreak + set list + fancy listchars" exe "set linebreak list listchars=nbsp:\u2423,tab:\u2595\u2014,trail:\u02d1,eol:\ub6" redraw! let line=ScreenChar(winwidth(0),4) call DoRecordScreen() + " let g:test ="Test 2: set nolinebreak list" set list nolinebreak redraw! let line=ScreenChar(winwidth(0),4) call DoRecordScreen() + " let g:test ="Test 3: set linebreak nolist" $put =\"\t*mask = nil;\" $ @@ -50,6 +54,7 @@ describe('linebreak', function() redraw! let line=ScreenChar(winwidth(0),4) call DoRecordScreen() + " let g:test ="Test 4: set linebreak list listchars and concealing" let c_defines=['#define ABCDE 1','#define ABCDEF 1','#define ABCDEFG 1','#define ABCDEFGH 1', '#define MSG_MODE_FILE 1','#define MSG_MODE_CONSOLE 2','#define MSG_MODE_FILE_AND_CONSOLE 3','#define MSG_MODE_FILE_THEN_CONSOLE 4'] call append('$', c_defines) @@ -61,6 +66,84 @@ describe('linebreak', function() redraw! let line=ScreenChar(winwidth(0),7) call DoRecordScreen() + " + let g:test ="Test 5: set linebreak list listchars and concealing part2" + let c_defines=['bbeeeeee ; some text'] + call append('$', c_defines) + $ + norm! zt + set nowrap ts=2 list linebreak listchars=tab:>- cole=2 concealcursor=n + syn clear + syn match meaning /;\s*\zs.*/ + syn match hasword /^\x\{8}/ contains=word + syn match word /\<\x\{8}\>/ contains=beginword,endword contained + syn match beginword /\<\x\x/ contained conceal + syn match endword /\x\{6}\>/ contained + hi meaning guibg=blue + hi beginword guibg=green + hi endword guibg=red + redraw! + let line=ScreenChar(winwidth(0),1) + call DoRecordScreen() + " + let g:test ="Test 6: Screenattributes for comment" + $put =g:test + call append('$', ' /* and some more */') + exe "set ft=c ts=7 linebreak list listchars=nbsp:\u2423,tab:\u2595\u2014,trail:\u02d1,eol:\ub6" + syntax on + hi SpecialKey term=underline ctermfg=red guifg=red + let attr=[] + nnoremap <expr> GG ":let attr += ['".screenattr(screenrow(),screencol())."']\n" + $ + norm! zt0 + ]]) + feed('GGlGGlGGlGGlGGlGGlGGlGGlGGlGGl') + source([[ + call append('$', ['ScreenAttributes for test6:']) + if attr[0] != attr[1] && attr[1] != attr[3] && attr[3] != attr[5] + call append('$', "Attribut 0 and 1 and 3 and 5 are different!") + else + call append('$', "Not all attributes are different") + endif + set cpo&vim linebreak selection=exclusive + let g:test ="Test 8: set linebreak with visual block mode and v_b_A and selection=exclusive and multibyte char" + $put =g:test + ]]) + feed("Golong line: <Esc>40afoobar <Esc>aTARGETÃ' at end<Esc>") + source([[ + exe "norm! $3B\<C-v>eAx\<Esc>" + " + let g:test ="Test 9: a multibyte sign and colorcolumn" + let attr=[] + let attr2=[] + $put ='' + $put ='a b c' + $put ='a b c' + set list nolinebreak cc=3 + ]]) + feed(':sign define foo text=<C-v>uff0b<CR>') + source([[ + sign place 1 name=foo line=50 buffer=2 + norm! 2kztj + let line1=line('.') + ]]) + feed('0GGlGGlGGlGGl') + source([[ + let line2=line('.') + let attr2=attr + let attr=[] + ]]) + feed('0GGlGGlGGlGGl') + source([[ + redraw! + let line=ScreenChar(winwidth(0),3) + call DoRecordScreen() + " expected: attr[2] is different because of colorcolumn + if attr[0] != attr2[0] || attr[1] != attr2[1] || attr[2] != attr2[2] + call append('$', "Screen attributes are different!") + else + call append('$', "Screen attributes are the same!") + endif ]]) -- Assert buffer contents. @@ -102,6 +185,25 @@ describe('linebreak', function() #define >CDEFGH>----1 #define >_FILE>--------->--->---1 #define >_CONSOLE>---------->---2 - #define >_FILE_AND_CONSOLE>---------3 ]]) + #define >_FILE_AND_CONSOLE>---------3 + bbeeeeee ; some text + + Test 5: set linebreak list listchars and concealing part2 + eeeeee>--->-;>some text + Test 6: Screenattributes for comment + /* and some more */ + ScreenAttributes for test6: + Attribut 0 and 1 and 3 and 5 are different! + Test 8: set linebreak with visual block mode and v_b_A and selection=exclusive and multibyte char + long line: foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar TARGETÃx' at end + + a b c + a b c + + Test 9: a multibyte sign and colorcolumn + ¶ + +a b c¶ + a b c¶ + Screen attributes are the same!]]) end) end) diff --git a/test/functional/legacy/mapping_spec.lua b/test/functional/legacy/mapping_spec.lua index 4d377904f9..a0d19926cb 100644 --- a/test/functional/legacy/mapping_spec.lua +++ b/test/functional/legacy/mapping_spec.lua @@ -2,12 +2,12 @@ local helpers = require('test.functional.helpers') local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local execute, expect = helpers.execute, helpers.expect +local execute, expect, wait = helpers.execute, helpers.expect, helpers.wait describe('mapping', function() before_each(clear) - it('is working', function() + it('abbreviations with р (0x80)', function() insert([[ test starts here: ]]) @@ -16,6 +16,46 @@ describe('mapping', function() execute('inoreab чкпр vim') feed('GAчкпр <esc>') + expect([[ + test starts here: + vim ]]) + end) + + it('Ctrl-c works in Insert mode', function() + -- Mapping of ctrl-c in insert mode + execute('set cpo-=< cpo-=k') + execute('inoremap <c-c> <ctrl-c>') + execute('cnoremap <c-c> dummy') + execute('cunmap <c-c>') + feed('GA<cr>') + feed('TEST2: CTRL-C |') + wait() + feed('<c-c>A|<cr><esc>') + wait() + execute('unmap <c-c>') + execute('unmap! <c-c>') + + expect([[ + + TEST2: CTRL-C |<ctrl-c>A| + ]]) + end) + + it('Ctrl-c works in Visual mode', function() + execute([[vnoremap <c-c> :<C-u>$put ='vmap works'<cr>]]) + feed('GV') + -- XXX: For some reason the mapping is only triggered + -- when <C-c> is in a separate feed command. + wait() + feed('<c-c>') + execute('vunmap <c-c>') + + expect([[ + + vmap works]]) + end) + + it('langmap', function() -- langmap should not get remapped in insert mode. execute('inoremap { FAIL_ilangmap') execute('set langmap=+{ langnoremap') @@ -37,14 +77,30 @@ describe('mapping', function() -- Assert buffer contents. expect([[ - test starts here: - vim + + + + +]]) end) + it('feedkeys', function() + insert([[ + a b c d + a b c d + ]]) + + -- Vim's issue #212 (feedkeys insert mapping at current position) + execute('nnoremap . :call feedkeys(".", "in")<cr>') + feed('/^a b<cr>') + feed('0qqdw.ifoo<esc>qj0@q<esc>') + execute('unmap .') + expect([[ + fooc d + fooc d + ]]) + end) + it('i_CTRL-G_U', function() -- <c-g>U<cursor> works only within a single line execute('imapclear') diff --git a/test/functional/legacy/marks_spec.lua b/test/functional/legacy/marks_spec.lua new file mode 100644 index 0000000000..8e9ceb1653 --- /dev/null +++ b/test/functional/legacy/marks_spec.lua @@ -0,0 +1,53 @@ +local helpers = require('test.functional.helpers') +local feed, insert, source = helpers.feed, helpers.insert, helpers.source +local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect + +describe('marks', function() + before_each(function() + clear() + end) + + it('restores a deleted mark after delete-undo-redo-undo', function() + insert([[ + + textline A + textline B + textline C + + Results:]]) + + execute([[:/^\t/+1]]) + feed([[maddu<C-R>u]]) + source([[ + let g:a = string(getpos("'a")) + $put ='Mark after delete-undo-redo-undo: '.g:a + ]]) + + expect([=[ + + textline A + textline B + textline C + + Results: + Mark after delete-undo-redo-undo: [0, 3, 2, 0]]=]) + end) + + it("CTRL-A and CTRL-X updates last changed mark '[, ']", function() + insert([[ + CTRL-A CTRL-X: + 123 123 123 + 123 123 123 + 123 123 123]]) + + source([[ + /^123/ + execute "normal! \<C-A>`[v`]rAjwvjw\<C-X>`[v`]rX"]]) + + expect([=[ + CTRL-A CTRL-X: + AAA 123 123 + 123 XXXXXXX + XXX 123 123]=]) + end) +end) diff --git a/test/functional/legacy/match_conceal_spec.lua b/test/functional/legacy/match_conceal_spec.lua new file mode 100644 index 0000000000..0ffa3cae7a --- /dev/null +++ b/test/functional/legacy/match_conceal_spec.lua @@ -0,0 +1,228 @@ +-- Test for matchadd() and conceal feature + +local helpers = require('test.functional.helpers') +local clear = helpers.clear +local expect = helpers.expect +local source = helpers.source + +describe('match_conceal', function() + before_each(function() + clear() + + source([[ + set wildchar=^E + 10new + vsp + vert resize 20 + put =\"\#\ This\ is\ a\ Test\" + norm! mazt + + fu! ScreenChar(width, lines) + let c='' + for j in range(1,a:lines) + for i in range(1,a:width) + let c.=nr2char(screenchar(j, i)) + endfor + let c.="\n" + endfor + return c + endfu + + fu! ScreenAttr(line, pos, eval) + let g:attr=[] + for col in a:pos + call add(g:attr, screenattr(a:line,col)) + endfor + " In case all values are zero, probably the terminal + " isn't set correctly, so catch that case + let null = (eval(join(g:attr, '+')) == 0) + let str=substitute(a:eval, '\d\+', 'g:attr[&]', 'g') + if null || eval(str) + let g:attr_test="OK: ". str + else + let g:attr_test="FAILED: ".str + let g:attr_test.="\n". join(g:attr, ' ') + let g:attr_test.="\n TERM: ". &term + endif + endfu + + fu! DoRecordScreen() + wincmd l + $put =printf(\"\n%s\", g:test) + $put =g:line + $put =g:attr_test + wincmd p + endfu + ]]) + end) + + it('is working', function() + source([=[ + let g:test ="Test 1: simple addmatch()" + call matchadd('Conceal', '\%2l ') + redraw! + let line=ScreenChar(winwidth(0),1) + call ScreenAttr(1,[1,2,7,10,12,16], "0!=1 && 1==2 && 1==3 && 1==4 && 0==5") + call DoRecordScreen() + + let g:test ="Test 2: simple addmatch() and conceal (should be: #XThisXisXaXTest)" + norm! 'azt + call clearmatches() + syntax on + set concealcursor=n conceallevel=1 + call matchadd('Conceal', '\%2l ', 10, -1, {'conceal': 'X'}) + redraw! + let line=ScreenChar(winwidth(0),1) + call ScreenAttr(1,[1,2,7,10,12,16], "0!=1 && 1==2 && 1==3 && 1==4 && 0==5") + call DoRecordScreen() + + let g:test ="Test 3: addmatch() and conceallevel=3 (should be: #ThisisaTest)" + norm! 'azt + set conceallevel=3 + call clearmatches() + call matchadd('Conceal', '\%2l ', 10, -1, {'conceal': 'X'}) + redraw! + let line=ScreenChar(winwidth(0),1) + call ScreenAttr(1,[1,2,7,10,12,16], "0==1 && 1==2 && 1==3 && 1==4 && 0!=5") + call DoRecordScreen() + + let g:test ="Test 4: more match() (should be: #Thisisa Test)" + norm! 'azt + call matchadd('ErrorMsg', '\%2l Test', 20, -1, {'conceal': 'X'}) + redraw! + let line=ScreenChar(winwidth(0),1) + call ScreenAttr(1,[1,2,7,10,12,16], "0==1 && 1==2 && 0!=3 && 3==4 && 0!=5 && 3!=5") + call DoRecordScreen() + + let g:test ="Test 5/1: default conceal char (should be: # This is a Test)" + norm! 'azt + call clearmatches() + set conceallevel=1 + call matchadd('Conceal', '\%2l ', 10, -1, {}) + redraw! + let line=ScreenChar(winwidth(0),1) + call ScreenAttr(1,[1,2,7,10,12,16], "0!=1 && 1==2 && 1==3 && 1==4 && 0==5") + call DoRecordScreen() + let g:test ="Test 5/2: default conceal char (should be: #+This+is+a+Test)" + norm! 'azt + set listchars=conceal:+ + redraw! + let line=ScreenChar(winwidth(0),1) + call ScreenAttr(1,[1,2,7,10,12,16], "0!=1 && 1==2 && 1==3 && 1==4 && 0==5") + call DoRecordScreen() + set listchars&vi + + let g:test ="Test 6/1: syn and match conceal (should be: #ZThisZisZaZTest)" + norm! 'azt + call clearmatches() + set conceallevel=1 + call matchadd('Conceal', '\%2l ', 10, -1, {'conceal': 'Z'}) + syn match MyConceal /\%2l / conceal containedin=ALL cchar=* + redraw! + let line=ScreenChar(winwidth(0),1) + call ScreenAttr(1,[1,2,7,10,12,16], "0!=1 && 1==2 && 1==3 && 1==4 && 0==5") + call DoRecordScreen() + let g:test ="Test 6/2: syn and match conceal (should be: #*This*is*a*Test)" + norm! 'azt + call clearmatches() + redraw! + let line=ScreenChar(winwidth(0),1) + call ScreenAttr(1,[1,2,7,10,12,16], "0!=1 && 1==2 && 1==3 && 1==4 && 0==5") + call DoRecordScreen() + + let g:test ="Test 7/1: clear matches" + norm! 'azt + syn on + call matchadd('Conceal', '\%2l ', 10, -1, {'conceal': 'Z'}) + let a=getmatches() + call clearmatches() + redraw! + let line=ScreenChar(winwidth(0),1) + call ScreenAttr(1,[1,2,7,10,12,16], "0==1 && 0==2 && 0==3 && 0==4 && 0==5") + call DoRecordScreen() + $put =a + call setmatches(a) + norm! 'azt + let g:test ="Test 7/2: reset match using setmatches()" + norm! 'azt + redraw! + let line=ScreenChar(winwidth(0),1) + call ScreenAttr(1,[1,2,7,10,12,16], "0!=1 && 1==2 && 1==3 && 1==4 && 0==5") + call DoRecordScreen() + + let g:test ="Test 8: using matchaddpos() (should be #Pis a Test" + norm! 'azt + call clearmatches() + call matchaddpos('Conceal', [[2,2,6]], 10, -1, {'conceal': 'P'}) + let a=getmatches() + redraw! + let line=ScreenChar(winwidth(0),1) + call ScreenAttr(1,[1,2,7,10,12,16], "0!=1 && 1!=2 && 0==2 && 0==3 && 0!=4 && 0!=5 && 4==5") + call DoRecordScreen() + $put =a + + let g:test ="Test 9: match using multibyte conceal char (should be: #ˑThisˑisˑaˑTest)" + norm! 'azt + call clearmatches() + call matchadd('Conceal', '\%2l ', 20, -1, {'conceal': "\u02d1"}) + redraw! + let line=ScreenChar(winwidth(0),1) + call ScreenAttr(1,[1,2,7,10,12,16], "0!=1 && 1==2 && 1==3 && 1==4 && 0==5") + call DoRecordScreen() + ]=]) + + expect([=[ + + # This is a Test + + Test 1: simple addmatch() + # This is a Test + OK: g:attr[0]!=g:attr[1] && g:attr[1]==g:attr[2] && g:attr[1]==g:attr[3] && g:attr[1]==g:attr[4] && g:attr[0]==g:attr[5] + + Test 2: simple addmatch() and conceal (should be: #XThisXisXaXTest) + #XThisXisXaXTest + OK: g:attr[0]!=g:attr[1] && g:attr[1]==g:attr[2] && g:attr[1]==g:attr[3] && g:attr[1]==g:attr[4] && g:attr[0]==g:attr[5] + + Test 3: addmatch() and conceallevel=3 (should be: #ThisisaTest) + #ThisisaTest + OK: g:attr[0]==g:attr[1] && g:attr[1]==g:attr[2] && g:attr[1]==g:attr[3] && g:attr[1]==g:attr[4] && g:attr[0]!=g:attr[5] + + Test 4: more match() (should be: #Thisisa Test) + #Thisisa Test + OK: g:attr[0]==g:attr[1] && g:attr[1]==g:attr[2] && g:attr[0]!=g:attr[3] && g:attr[3]==g:attr[4] && g:attr[0]!=g:attr[5] && g:attr[3]!=g:attr[5] + + Test 5/1: default conceal char (should be: # This is a Test) + # This is a Test + OK: g:attr[0]!=g:attr[1] && g:attr[1]==g:attr[2] && g:attr[1]==g:attr[3] && g:attr[1]==g:attr[4] && g:attr[0]==g:attr[5] + + Test 5/2: default conceal char (should be: #+This+is+a+Test) + #+This+is+a+Test + OK: g:attr[0]!=g:attr[1] && g:attr[1]==g:attr[2] && g:attr[1]==g:attr[3] && g:attr[1]==g:attr[4] && g:attr[0]==g:attr[5] + + Test 6/1: syn and match conceal (should be: #ZThisZisZaZTest) + #ZThisZisZaZTest + OK: g:attr[0]!=g:attr[1] && g:attr[1]==g:attr[2] && g:attr[1]==g:attr[3] && g:attr[1]==g:attr[4] && g:attr[0]==g:attr[5] + + Test 6/2: syn and match conceal (should be: #*This*is*a*Test) + #*This*is*a*Test + OK: g:attr[0]!=g:attr[1] && g:attr[1]==g:attr[2] && g:attr[1]==g:attr[3] && g:attr[1]==g:attr[4] && g:attr[0]==g:attr[5] + + Test 7/1: clear matches + # This is a Test + OK: g:attr[0]==g:attr[1] && g:attr[0]==g:attr[2] && g:attr[0]==g:attr[3] && g:attr[0]==g:attr[4] && g:attr[0]==g:attr[5] + {'group': 'Conceal', 'pattern': '\%2l ', 'priority': 10, 'id': 10, 'conceal': 'Z'} + + Test 7/2: reset match using setmatches() + #ZThisZisZaZTest + OK: g:attr[0]!=g:attr[1] && g:attr[1]==g:attr[2] && g:attr[1]==g:attr[3] && g:attr[1]==g:attr[4] && g:attr[0]==g:attr[5] + + Test 8: using matchaddpos() (should be #Pis a Test + #Pis a Test + OK: g:attr[0]!=g:attr[1] && g:attr[1]!=g:attr[2] && g:attr[0]==g:attr[2] && g:attr[0]==g:attr[3] && g:attr[0]!=g:attr[4] && g:attr[0]!=g:attr[5] && g:attr[4]==g:attr[5] + {'group': 'Conceal', 'id': 11, 'priority': 10, 'pos1': [2, 2, 6], 'conceal': 'P'} + + Test 9: match using multibyte conceal char (should be: #ˑThisˑisˑaˑTest) + #ˑThisˑisˑaˑTest + OK: g:attr[0]!=g:attr[1] && g:attr[1]==g:attr[2] && g:attr[1]==g:attr[3] && g:attr[1]==g:attr[4] && g:attr[0]==g:attr[5]]=]) + end) +end) diff --git a/test/functional/legacy/nested_function_spec.lua b/test/functional/legacy/nested_function_spec.lua index 87371c8294..fac3b03191 100644 --- a/test/functional/legacy/nested_function_spec.lua +++ b/test/functional/legacy/nested_function_spec.lua @@ -1,7 +1,7 @@ -- Tests for nested function. local helpers = require('test.functional.helpers') -local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert +local clear, insert = helpers.clear, helpers.insert local execute, expect, source = helpers.execute, helpers.expect, helpers.source describe('test_nested_function', function() diff --git a/test/functional/legacy/options_spec.lua b/test/functional/legacy/options_spec.lua index 773acb9663..21e99c4aa1 100644 --- a/test/functional/legacy/options_spec.lua +++ b/test/functional/legacy/options_spec.lua @@ -1,13 +1,27 @@ --- Test if ":options" throws any exception. The options window seems to mess --- other tests, so restart nvim in the teardown hook - local helpers = require('test.functional.helpers') local command, clear = helpers.command, helpers.clear +local source, expect = helpers.source, helpers.expect describe('options', function() setup(clear) - it('is working', function() + it('should not throw any exception', function() command('options') end) end) + +describe('set', function() + setup(clear) + + it("should keep two comma when 'path' is changed", function() + source([[ + set path=foo,,bar + set path-=bar + set path+=bar + $put =&path]]) + + expect([[ + + foo,,bar]]) + end) +end) diff --git a/test/functional/legacy/qf_title_spec.lua b/test/functional/legacy/qf_title_spec.lua index aa005117be..01c781cc05 100644 --- a/test/functional/legacy/qf_title_spec.lua +++ b/test/functional/legacy/qf_title_spec.lua @@ -1,8 +1,8 @@ -- Tests for quickfix window's title local helpers = require('test.functional.helpers') -local feed, insert, source = helpers.feed, helpers.insert, helpers.source -local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect +local insert, source = helpers.insert, helpers.source +local clear, expect = helpers.clear, helpers.expect describe('qf_title', function() setup(clear) diff --git a/test/functional/legacy/quickfix_spec.lua b/test/functional/legacy/quickfix_spec.lua new file mode 100644 index 0000000000..88f86815b3 --- /dev/null +++ b/test/functional/legacy/quickfix_spec.lua @@ -0,0 +1,17 @@ +-- Test for the quickfix commands. + +local helpers = require('test.functional.helpers') +local source, clear = helpers.source, helpers.clear + +describe('helpgrep', function() + before_each(clear) + + it('works', function() + source([[ + helpgrep quickfix + copen + " This wipes out the buffer, make sure that doesn't cause trouble. + cclose + ]]) + end) +end) diff --git a/test/functional/legacy/search_mbyte_spec.lua b/test/functional/legacy/search_mbyte_spec.lua new file mode 100644 index 0000000000..075b24b897 --- /dev/null +++ b/test/functional/legacy/search_mbyte_spec.lua @@ -0,0 +1,26 @@ +local helpers = require('test.functional.helpers') +local insert = helpers.insert +local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect + +describe('search_mbyte', function() + before_each(clear) + + it("search('multi-byte char', 'bce')", function() + insert([=[ + Results: + + Test bce: + A]=]) + + execute('/^Test bce:/+1') + execute([[$put =search('A', 'bce', line('.'))]]) + + -- Assert buffer contents. + expect([=[ + Results: + + Test bce: + A + 4]=]) + end) +end) diff --git a/test/functional/legacy/searchpos_spec.lua b/test/functional/legacy/searchpos_spec.lua new file mode 100644 index 0000000000..1c9b1ccee6 --- /dev/null +++ b/test/functional/legacy/searchpos_spec.lua @@ -0,0 +1,35 @@ +local helpers = require('test.functional.helpers') +local call = helpers.call +local clear = helpers.clear +local execute = helpers.execute +local eq = helpers.eq +local eval = helpers.eval +local insert = helpers.insert + +describe('searchpos', function() + before_each(clear) + + it('is working', function() + insert([[ + 1a3 + 123xyz]]) + + call('cursor', 1, 1) + eq({1, 1, 2}, eval([[searchpos('\%(\([a-z]\)\|\_.\)\{-}xyz', 'pcW')]])) + call('cursor', 1, 2) + eq({2, 1, 1}, eval([[searchpos('\%(\([a-z]\)\|\_.\)\{-}xyz', 'pcW')]])) + + execute('set cpo-=c') + call('cursor', 1, 2) + eq({1, 2, 2}, eval([[searchpos('\%(\([a-z]\)\|\_.\)\{-}xyz', 'pcW')]])) + call('cursor', 1, 3) + eq({1, 3, 1}, eval([[searchpos('\%(\([a-z]\)\|\_.\)\{-}xyz', 'pcW')]])) + + -- Now with \zs, first match is in column 0, "a" is matched. + call('cursor', 1, 3) + eq({2, 4, 2}, eval([[searchpos('\%(\([a-z]\)\|\_.\)\{-}\zsxyz', 'pcW')]])) + -- With z flag start at cursor column, don't see the "a". + call('cursor', 1, 3) + eq({2, 4, 1}, eval([[searchpos('\%(\([a-z]\)\|\_.\)\{-}\zsxyz', 'pcWz')]])) + end) +end) diff --git a/test/functional/legacy/set_spec.lua b/test/functional/legacy/set_spec.lua new file mode 100644 index 0000000000..f81fcd3700 --- /dev/null +++ b/test/functional/legacy/set_spec.lua @@ -0,0 +1,15 @@ +-- Tests for :set + +local helpers = require('test.functional.helpers') +local clear, execute, eval, eq = + helpers.clear, helpers.execute, helpers.eval, helpers.eq + +describe(':set', function() + before_each(clear) + + it('recognizes a trailing comma with +=', function() + execute('set wildignore=*.png,') + execute('set wildignore+=*.jpg') + eq('*.png,*.jpg', eval('&wildignore')) + end) +end) diff --git a/test/functional/legacy/signs_spec.lua b/test/functional/legacy/signs_spec.lua index 89b2bf3b73..5a834c39e3 100644 --- a/test/functional/legacy/signs_spec.lua +++ b/test/functional/legacy/signs_spec.lua @@ -1,7 +1,6 @@ -- Tests for signs local helpers = require('test.functional.helpers') -local feed, insert, source = helpers.feed, helpers.insert, helpers.source local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect describe('signs', function() diff --git a/test/functional/legacy/tagcase_spec.lua b/test/functional/legacy/tagcase_spec.lua new file mode 100644 index 0000000000..9a8c6fbe42 --- /dev/null +++ b/test/functional/legacy/tagcase_spec.lua @@ -0,0 +1,150 @@ +local helpers = require('test.functional.helpers') +local clear = helpers.clear +local eq = helpers.eq +local eval = helpers.eval +local exc_exec = helpers.exc_exec +local expect = helpers.expect +local insert = helpers.insert +local source = helpers.source +local write_file = helpers.write_file + +describe("'tagcase' option", function() + setup(function() + write_file('Xtags', [[ + Bar Xtext 3 + Foo Xtext 2 + foo Xtext 4]]) + end) + + before_each(function() + clear() + source([[ + lang mess C + set tags=Xtags]]) + end) + + teardown(function() + os.remove('Xtags') + end) + + it('should have correct default values', function() + source([[ + set ic& + setg tc& + setl tc& + ]]) + + eq(0, eval('&ic')) + eq('followic', eval('&g:tc')) + eq('followic', eval('&l:tc')) + eq('followic', eval('&tc')) + end) + + it('should accept <empty> only for setlocal', function() + -- Verify that the local setting accepts <empty> but that the global setting + -- does not. The first of these (setting the local value to <empty>) should + -- succeed; the other two should fail. + eq(0, exc_exec('setl tc=')) + eq('Vim(setglobal):E474: Invalid argument: tc=', exc_exec('setg tc=')) + eq('Vim(set):E474: Invalid argument: tc=', exc_exec('set tc=')) + end) + + it("should work with 'ignorecase' correctly in all combinations", function() + -- Verify that the correct number of matching tags is found for all values of + -- 'ignorecase' and global and local values 'tagcase', in all combinations. + insert([[ + + Foo + Bar + foo + + end text]]) + + source([[ + for &ic in [0, 1] + for &g:tc in ["followic", "ignore", "match"] + for &l:tc in ["", "followic", "ignore", "match"] + call append('$', "ic=".&ic." g:tc=".&g:tc." l:tc=".&l:tc." tc=".&tc) + call append('$', len(taglist("^foo$"))) + call append('$', len(taglist("^Foo$"))) + endfor + endfor + endfor + + 1,/^end text$/d]]) + + expect([[ + ic=0 g:tc=followic l:tc= tc=followic + 1 + 1 + ic=0 g:tc=followic l:tc=followic tc=followic + 1 + 1 + ic=0 g:tc=followic l:tc=ignore tc=ignore + 2 + 2 + ic=0 g:tc=followic l:tc=match tc=match + 1 + 1 + ic=0 g:tc=ignore l:tc= tc=ignore + 2 + 2 + ic=0 g:tc=ignore l:tc=followic tc=followic + 1 + 1 + ic=0 g:tc=ignore l:tc=ignore tc=ignore + 2 + 2 + ic=0 g:tc=ignore l:tc=match tc=match + 1 + 1 + ic=0 g:tc=match l:tc= tc=match + 1 + 1 + ic=0 g:tc=match l:tc=followic tc=followic + 1 + 1 + ic=0 g:tc=match l:tc=ignore tc=ignore + 2 + 2 + ic=0 g:tc=match l:tc=match tc=match + 1 + 1 + ic=1 g:tc=followic l:tc= tc=followic + 2 + 2 + ic=1 g:tc=followic l:tc=followic tc=followic + 2 + 2 + ic=1 g:tc=followic l:tc=ignore tc=ignore + 2 + 2 + ic=1 g:tc=followic l:tc=match tc=match + 1 + 1 + ic=1 g:tc=ignore l:tc= tc=ignore + 2 + 2 + ic=1 g:tc=ignore l:tc=followic tc=followic + 2 + 2 + ic=1 g:tc=ignore l:tc=ignore tc=ignore + 2 + 2 + ic=1 g:tc=ignore l:tc=match tc=match + 1 + 1 + ic=1 g:tc=match l:tc= tc=match + 1 + 1 + ic=1 g:tc=match l:tc=followic tc=followic + 2 + 2 + ic=1 g:tc=match l:tc=ignore tc=ignore + 2 + 2 + ic=1 g:tc=match l:tc=match tc=match + 1 + 1]]) + end) +end) diff --git a/test/functional/legacy/textobjects_spec.lua b/test/functional/legacy/textobjects_spec.lua new file mode 100644 index 0000000000..1e8e0b0bcb --- /dev/null +++ b/test/functional/legacy/textobjects_spec.lua @@ -0,0 +1,61 @@ +local helpers = require('test.functional.helpers') +local call = helpers.call +local clear = helpers.clear +local execute = helpers.execute +local expect = helpers.expect +local source = helpers.source + +describe('Text object', function() + before_each(function() + clear() + execute('set shada=') + source([[ + function SelectionOut(data) + new + call setline(1, a:data) + call setreg('"', '') + normal! ggfrmavi)y + $put =getreg('\"') + call setreg('"', '') + normal! `afbmavi)y + $put =getreg('\"') + call setreg('"', '') + normal! `afgmavi)y + $put =getreg('\"') + endfunction + ]]) + end) + + it('Test for vi) without cpo-M', function() + execute('set cpo-=M') + call('SelectionOut', '(red \\(blue) green)') + + expect([[ + (red \(blue) green) + red \(blue + red \(blue + ]]) + end) + + it('Test for vi) with cpo-M #1', function() + execute('set cpo+=M') + call('SelectionOut', '(red \\(blue) green)') + + expect([[ + (red \(blue) green) + red \(blue) green + blue + red \(blue) green]]) + end) + + it('Test for vi) with cpo-M #2', function() + execute('set cpo+=M') + call('SelectionOut', '(red (blue\\) green)') + + expect([[ + (red (blue\) green) + red (blue\) green + blue\ + red (blue\) green]]) + end) +end) diff --git a/test/functional/legacy/undolevels_spec.lua b/test/functional/legacy/undolevels_spec.lua new file mode 100644 index 0000000000..41274b3a04 --- /dev/null +++ b/test/functional/legacy/undolevels_spec.lua @@ -0,0 +1,58 @@ +local helpers = require('test.functional.helpers') +local source, clear = helpers.source, helpers.clear +local eq, nvim = helpers.eq, helpers.meths + +describe('undolevel', function() + setup(clear) + + it('is working', function() + source([[ + 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) + + endfunc + + call Test_global_local_undolevels() + ]]) + + eq({}, nvim.get_vvar('errors')) + end) +end) diff --git a/test/functional/legacy/utf8_spec.lua b/test/functional/legacy/utf8_spec.lua index ef717042d0..d33ba6b5fd 100644 --- a/test/functional/legacy/utf8_spec.lua +++ b/test/functional/legacy/utf8_spec.lua @@ -3,9 +3,11 @@ local helpers = require('test.functional.helpers') local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert local execute, expect = helpers.execute, helpers.expect +local eq, eval = helpers.eq, helpers.eval +local source = helpers.source describe('utf8', function() - setup(clear) + before_each(clear) it('is working', function() insert('start:') @@ -27,4 +29,55 @@ describe('utf8', function() xあああ bxbb]]) end) + + it('strchars()', function() + eq(1, eval('strchars("a")')) + eq(1, eval('strchars("a", 0)')) + eq(1, eval('strchars("a", 1)')) + + eq(3, eval('strchars("あいa")')) + eq(3, eval('strchars("あいa", 0)')) + eq(3, eval('strchars("あいa", 1)')) + + eq(2, eval('strchars("A\\u20dd")')) + eq(2, eval('strchars("A\\u20dd", 0)')) + eq(1, eval('strchars("A\\u20dd", 1)')) + + eq(3, eval('strchars("A\\u20dd\\u20dd")')) + eq(3, eval('strchars("A\\u20dd\\u20dd", 0)')) + eq(1, eval('strchars("A\\u20dd\\u20dd", 1)')) + + eq(1, eval('strchars("\\u20dd")')) + eq(1, eval('strchars("\\u20dd", 0)')) + eq(1, eval('strchars("\\u20dd", 1)')) + end) + + it('customlist completion', function() + source([[ + function! CustomComplete1(lead, line, pos) + return ['あ', 'い'] + endfunction + command -nargs=1 -complete=customlist,CustomComplete1 Test1 echo]]) + feed(":Test1 <C-L>'<C-B>$put='<CR>") + + source([[ + function! CustomComplete2(lead, line, pos) + return ['あたし', 'あたま', 'あたりめ'] + endfunction + command -nargs=1 -complete=customlist,CustomComplete2 Test2 echo]]) + feed(":Test2 <C-L>'<C-B>$put='<CR>") + + source([[ + function! CustomComplete3(lead, line, pos) + return ['Nこ', 'Nん', 'Nぶ'] + endfunction + command -nargs=1 -complete=customlist,CustomComplete3 Test3 echo]]) + feed(":Test3 <C-L>'<C-B>$put='<CR>") + + expect([[ + + Test1 + Test2 あた + Test3 N]]) + end) end) diff --git a/test/functional/legacy/wordcount_spec.lua b/test/functional/legacy/wordcount_spec.lua new file mode 100644 index 0000000000..ba7be8f21b --- /dev/null +++ b/test/functional/legacy/wordcount_spec.lua @@ -0,0 +1,171 @@ +-- Test for wordcount() function + +local helpers = require('test.functional.helpers') +local feed, insert, source = helpers.feed, helpers.insert, helpers.source +local clear, execute = helpers.clear, helpers.execute +local eq, eval = helpers.eq, helpers.eval + +describe('wordcount', function() + before_each(clear) + + it('is working', function() + execute('set selection=inclusive') + execute('fileformat=unix') + execute('fileformats=unix') + + insert([=[ + RESULT test:]=]) + + execute('new') + source([=[ + function DoRecordWin(...) + wincmd k + if exists("a:1") + call cursor(a:1) + endif + let result=[] + call add(result, getline(1, '$')) + call add(result, wordcount()) + wincmd j + return result + endfunction + ]=]) + + source([=[ + function PutInWindow(args) + wincmd k + %d _ + call append(1, a:args) + wincmd j + endfunction + ]=]) + + source([=[ + function! STL() + if mode() =~? 'V' + let g:visual_stat=wordcount() + endif + return string(wordcount()) + endfunction + ]=]) + + -- Test 1: empty window + eq(eval('DoRecordWin()'), + eval([=[ + [[''], {'chars': 0, 'cursor_chars': 0, 'words': 0, 'cursor_words': 0, 'bytes': 0, 'cursor_bytes': 0}] + ]=]) + ) + + -- Test 2: some words, cursor at start + execute([[call PutInWindow('one two three')]]) + eq(eval('DoRecordWin([1, 1, 0])'), + eval([=[ + [['', 'one two three'], {'chars': 15, 'cursor_chars': 1, 'words': 3, 'cursor_words': 0, 'bytes': 15, 'cursor_bytes': 1}] + ]=]) + ) + + -- Test 3: some words, cursor at end + execute([[call PutInWindow('one two three')]]) + eq(eval('DoRecordWin([2, 99, 0])'), + eval([=[ + [['', 'one two three'], {'chars': 15, 'cursor_chars': 14, 'words': 3, 'cursor_words': 3, 'bytes': 15, 'cursor_bytes': 14}] + ]=]) + ) + + -- Test 4: some words, cursor at end, ve=all + execute('set ve=all') + execute([[call PutInWindow('one two three')]]) + eq(eval('DoRecordWin([2,99,0])'), + eval([=[ + [['', 'one two three'], {'chars': 15, 'cursor_chars': 15, 'words': 3, 'cursor_words': 3, 'bytes': 15, 'cursor_bytes': 15}] + ]=]) + ) + execute('set ve=') + + -- Test 5: several lines with words + execute([=[call PutInWindow(['one two three', 'one two three', 'one two three'])]=]) + eq(eval('DoRecordWin([4,99,0])'), + eval([=[ + [['', 'one two three', 'one two three', 'one two three'], {'chars': 43, 'cursor_chars': 42, 'words': 9, 'cursor_words': 9, 'bytes': 43, 'cursor_bytes': 42}] + ]=]) + ) + + -- Test 6: one line with BOM set + execute([[call PutInWindow('one two three')]]) + execute('wincmd k') + execute('set bomb') + execute('wincmd j') + eq(eval('DoRecordWin([2,99,0])'), + eval([=[ + [['', 'one two three'], {'chars': 15, 'cursor_chars': 14, 'words': 3, 'cursor_words': 3, 'bytes': 18, 'cursor_bytes': 14}] + ]=]) + ) + execute('wincmd k') + execute('set nobomb') + execute('wincmd j') + + -- Test 7: one line with multibyte words + execute([=[call PutInWindow(['Äne M¤ne Müh'])]=]) + eq(eval('DoRecordWin([2,99,0])'), + eval([=[ + [['', 'Äne M¤ne Müh'], {'chars': 14, 'cursor_chars': 13, 'words': 3, 'cursor_words': 3, 'bytes': 17, 'cursor_bytes': 16}] + ]=]) + ) + + -- Test 8: several lines with multibyte words + execute([=[call PutInWindow(['Äne M¤ne Müh', 'und raus bist dü!'])]=]) + eq(eval('DoRecordWin([3,99,0])'), + eval([=[ + [['', 'Äne M¤ne Müh', 'und raus bist dü!'], {'chars': 32, 'cursor_chars': 31, 'words': 7, 'cursor_words': 7, 'bytes': 36, 'cursor_bytes': 35}] + ]=]) + ) + + -- Test 9: visual mode, complete buffer + execute([=[call PutInWindow(['Äne M¤ne Müh', 'und raus bist dü!'])]=]) + execute('wincmd k') + execute('set ls=2 stl=%{STL()}') + -- -- Start visual mode quickly and select complete buffer. + execute('0') + feed('V2jy<cr>') + execute('set stl= ls=1') + execute('let log=DoRecordWin([3,99,0])') + execute('let log[1]=g:visual_stat') + eq(eval('log'), + eval([=[ + [['', 'Äne M¤ne Müh', 'und raus bist dü!'], {'chars': 32, 'words': 7, 'bytes': 36, 'visual_chars': 32, 'visual_words': 7, 'visual_bytes': 36}] + ]=]) + ) + + -- Test 10: visual mode (empty) + execute([=[call PutInWindow(['Äne M¤ne Müh', 'und raus bist dü!'])]=]) + execute('wincmd k') + execute('set ls=2 stl=%{STL()}') + -- Start visual mode quickly and select complete buffer. + execute('0') + feed('v$y<cr>') + execute('set stl= ls=1') + execute('let log=DoRecordWin([3,99,0])') + execute('let log[1]=g:visual_stat') + eq(eval('log'), + eval([=[ + [['', 'Äne M¤ne Müh', 'und raus bist dü!'], {'chars': 32, 'words': 7, 'bytes': 36, 'visual_chars': 1, 'visual_words': 0, 'visual_bytes': 1}] + ]=]) + ) + + -- Test 11: visual mode, single line + execute([=[call PutInWindow(['Äne M¤ne Müh', 'und raus bist dü!'])]=]) + execute('wincmd k') + execute('set ls=2 stl=%{STL()}') + -- Start visual mode quickly and select complete buffer. + execute('2') + feed('0v$y<cr>') + execute('set stl= ls=1') + execute('let log=DoRecordWin([3,99,0])') + execute('let log[1]=g:visual_stat') + eq(eval('log'), + eval([=[ + [['', 'Äne M¤ne Müh', 'und raus bist dü!'], {'chars': 32, 'words': 7, 'bytes': 36, 'visual_chars': 13, 'visual_words': 3, 'visual_bytes': 16}] + ]=]) + ) + end) +end) diff --git a/test/functional/legacy/writefile_spec.lua b/test/functional/legacy/writefile_spec.lua index e7a260bcd9..efdfc1d09f 100644 --- a/test/functional/legacy/writefile_spec.lua +++ b/test/functional/legacy/writefile_spec.lua @@ -1,7 +1,6 @@ -- Tests for writefile() local helpers = require('test.functional.helpers') -local feed, insert, source = helpers.feed, helpers.insert, helpers.source local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect describe('writefile', function() diff --git a/test/functional/normal/K_spec.lua b/test/functional/normal/K_spec.lua index dbda6dcb93..df6b429f50 100644 --- a/test/functional/normal/K_spec.lua +++ b/test/functional/normal/K_spec.lua @@ -1,7 +1,6 @@ local helpers = require('test.functional.helpers') -local execute, eq, clear, eval, feed, ok = - helpers.execute, helpers.eq, helpers.clear, helpers.eval, - helpers.feed, helpers.ok +local eq, clear, eval, feed = + helpers.eq, helpers.clear, helpers.eval, helpers.feed describe('K', function() local test_file = 'K_spec_out' diff --git a/test/functional/options/defaults_spec.lua b/test/functional/options/defaults_spec.lua new file mode 100644 index 0000000000..d4c3267997 --- /dev/null +++ b/test/functional/options/defaults_spec.lua @@ -0,0 +1,84 @@ +local helpers = require('test.functional.helpers') +local Screen = require('test.functional.ui.screen') +local clear, eval, eq = helpers.clear, helpers.eval, helpers.eq +local execute = helpers.execute + +local function init_session(...) + local args = { helpers.nvim_prog, '-i', 'NONE', '--embed', + '--cmd', 'set shortmess+=I background=light noswapfile noautoindent', + '--cmd', 'set laststatus=1 undodir=. directory=. viewdir=. backupdir=.' + } + for _, v in ipairs({...}) do + table.insert(args, v) + end + helpers.set_session(helpers.spawn(args)) +end + +describe('startup defaults', function() + before_each(function() + clear() + end) + + describe(':filetype', function() + local function expect_filetype(expected) + local screen = Screen.new(48, 4) + screen:attach() + execute('filetype') + screen:expect([[ + ^ | + ~ | + ~ | + ]]..expected + ) + end + + it('enabled by `-u NORC`', function() + init_session('-u', 'NORC') + expect_filetype( + 'filetype detection:ON plugin:ON indent:ON |') + end) + + it('disabled by `-u NONE`', function() + init_session('-u', 'NONE') + expect_filetype( + 'filetype detection:OFF plugin:OFF indent:OFF |') + end) + + it('overridden by early `filetype on`', function() + init_session('-u', 'NORC', '--cmd', 'filetype on') + expect_filetype( + 'filetype detection:ON plugin:OFF indent:OFF |') + end) + + it('overridden by early `filetype plugin on`', function() + init_session('-u', 'NORC', '--cmd', 'filetype plugin on') + expect_filetype( + 'filetype detection:ON plugin:ON indent:OFF |') + end) + + it('overridden by early `filetype indent on`', function() + init_session('-u', 'NORC', '--cmd', 'filetype indent on') + expect_filetype( + 'filetype detection:ON plugin:OFF indent:ON |') + end) + end) + + describe('syntax', function() + it('enabled by `-u NORC`', function() + init_session('-u', 'NORC') + eq(1, eval('g:syntax_on')) + end) + + it('disabled by `-u NONE`', function() + init_session('-u', 'NONE') + eq(0, eval('exists("g:syntax_on")')) + end) + + it('overridden by early `syntax off`', function() + init_session('-u', 'NORC', '--cmd', 'syntax off') + eq(0, eval('exists("g:syntax_on")')) + end) + end) +end) + + diff --git a/test/functional/options/shortmess_spec.lua b/test/functional/options/shortmess_spec.lua new file mode 100644 index 0000000000..4455ef663f --- /dev/null +++ b/test/functional/options/shortmess_spec.lua @@ -0,0 +1,39 @@ +local helpers = require('test.functional.helpers') +local Screen = require('test.functional.ui.screen') +local clear, execute = helpers.clear, helpers.execute + +describe("'shortmess'", function() + local screen + + before_each(function() + clear() + screen = Screen.new(25, 5) + screen:attach() + end) + + after_each(function() + screen:detach() + end) + + describe('"F" flag', function() + it('hides messages about the files read', function() + execute('e test') + screen:expect([[ + ^ | + ~ | + ~ | + ~ | + "test" is a directory | + ]]) + execute('set shortmess=F') + execute('e test') + screen:expect([[ + ^ | + ~ | + ~ | + ~ | + :e test | + ]]) + end) + end) +end) diff --git a/test/functional/plugin/helpers.lua b/test/functional/plugin/helpers.lua new file mode 100644 index 0000000000..5b6ea88c34 --- /dev/null +++ b/test/functional/plugin/helpers.lua @@ -0,0 +1,41 @@ +local paths = require('test.config.paths') + +local helpers = require('test.functional.helpers') +local spawn, set_session, nvim_prog, merge_args = + helpers.spawn, helpers.set_session, helpers.nvim_prog, helpers.merge_args + +local additional_cmd = '' + +local function nvim_argv(shada_file) + local rtp_value = ('\'%s/runtime\''):format( + paths.test_source_path:gsub('\'', '\'\'')) + local nvim_args = {nvim_prog, '-u', 'NORC', '-i', shada_file or 'NONE', '-N', + '--cmd', 'set shortmess+=I background=light noswapfile', + '--cmd', 'let &runtimepath=' .. rtp_value, + '--cmd', additional_cmd, + '--embed'} + if helpers.prepend_argv then + return merge_args(helpers.prepend_argv, nvim_args) + else + return nvim_args + end +end + +local session = nil + +local reset = function(...) + if session then + session:close() + end + session = spawn(nvim_argv(...)) + set_session(session) +end + +local set_additional_cmd = function(s) + additional_cmd = s +end + +return { + reset=reset, + set_additional_cmd=set_additional_cmd, +} diff --git a/test/functional/plugin/matchparen_spec.lua b/test/functional/plugin/matchparen_spec.lua new file mode 100644 index 0000000000..d8c1f2d392 --- /dev/null +++ b/test/functional/plugin/matchparen_spec.lua @@ -0,0 +1,36 @@ +local helpers = require('test.functional.helpers') +local Screen = require('test.functional.ui.screen') +local clear, feed, execute = helpers.clear, helpers.feed, helpers.execute + +describe('matchparen', function() + local screen + + before_each(function() + clear() + screen = Screen.new(20,5) + screen:attach() + screen:set_default_attr_ignore( {{bold=true, foreground=Screen.colors.Blue}} ) + end) + + it('uses correct column after i_<Up>. Vim patch 7.4.1296', function() + execute('set noai nosi nocin') + execute('runtime plugin/matchparen.vim') + feed('ivoid f_test()<cr>') + feed('{<cr>') + feed('}') + + -- critical part: up + cr should result in an empty line inbetween the + -- brackets... if the bug is there, the empty line will be before the '{' + feed('<up>') + feed('<cr>') + + screen:expect([[ + void f_test() | + { | + ^ | + } | + {1:-- INSERT --} | + ]], {[1] = {bold = true}}) + + end) +end) diff --git a/test/functional/plugin/msgpack_spec.lua b/test/functional/plugin/msgpack_spec.lua new file mode 100644 index 0000000000..60ba88e55b --- /dev/null +++ b/test/functional/plugin/msgpack_spec.lua @@ -0,0 +1,714 @@ +local helpers = require('test.functional.helpers') +local meths = helpers.meths +local eq, nvim_eval, nvim_command, exc_exec = + helpers.eq, helpers.eval, helpers.command, helpers.exc_exec +local ok = helpers.ok +local NIL = helpers.NIL + +local plugin_helpers = require('test.functional.plugin.helpers') +local reset = plugin_helpers.reset + +describe('In autoload/msgpack.vim', function() + before_each(reset) + + local sp = function(typ, val) + return ('{"_TYPE": v:msgpack_types.%s, "_VAL": %s}'):format(typ, val) + end + local mapsp = function(...) + local val = '' + for i=1,(select('#', ...)/2) do + val = ('%s[%s,%s],'):format(val, select(i * 2 - 1, ...), + select(i * 2, ...)) + end + return sp('map', '[' .. val .. ']') + end + + local nan = -(1.0/0.0-1.0/0.0) + local inf = 1.0/0.0 + local minus_inf = -(1.0/0.0) + + describe('function msgpack#equal', function() + local msgpack_eq = function(expected, a, b) + eq(expected, nvim_eval(('msgpack#equal(%s, %s)'):format(a, b))) + if a ~= b then + eq(expected, nvim_eval(('msgpack#equal(%s, %s)'):format(b, a))) + end + end + it('compares raw integers correctly', function() + msgpack_eq(1, '1', '1') + msgpack_eq(0, '1', '0') + end) + it('compares integer specials correctly', function() + msgpack_eq(1, sp('integer', '[-1, 1, 0, 0]'), + sp('integer', '[-1, 1, 0, 0]')) + msgpack_eq(0, sp('integer', '[-1, 1, 0, 0]'), + sp('integer', '[ 1, 1, 0, 0]')) + end) + it('compares integer specials with raw integer correctly', function() + msgpack_eq(1, sp('integer', '[-1, 0, 0, 1]'), '-1') + msgpack_eq(0, sp('integer', '[-1, 0, 0, 1]'), '1') + msgpack_eq(0, sp('integer', '[ 1, 0, 0, 1]'), '-1') + msgpack_eq(1, sp('integer', '[ 1, 0, 0, 1]'), '1') + end) + it('compares integer with float correctly', function() + msgpack_eq(0, '0', '0.0') + end) + it('compares raw binaries correctly', function() + msgpack_eq(1, '"abc\\ndef"', '"abc\\ndef"') + msgpack_eq(0, '"abc\\ndef"', '"abc\\nghi"') + end) + it('compares binary specials correctly', function() + msgpack_eq(1, sp('binary', '["abc\\n", "def"]'), + sp('binary', '["abc\\n", "def"]')) + msgpack_eq(0, sp('binary', '["abc", "def"]'), + sp('binary', '["abc\\n", "def"]')) + end) + it('compares binary specials with raw binaries correctly', function() + msgpack_eq(1, sp('binary', '["abc", "def"]'), '"abc\\ndef"') + msgpack_eq(0, sp('binary', '["abc", "def"]'), '"abcdef"') + end) + it('compares string specials correctly', function() + msgpack_eq(1, sp('string', '["abc\\n", "def"]'), + sp('string', '["abc\\n", "def"]')) + msgpack_eq(0, sp('string', '["abc", "def"]'), + sp('string', '["abc\\n", "def"]')) + end) + it('compares string specials with binary correctly', function() + msgpack_eq(0, sp('string', '["abc\\n", "def"]'), + sp('binary', '["abc\\n", "def"]')) + msgpack_eq(0, sp('string', '["abc", "def"]'), '"abc\\ndef"') + msgpack_eq(0, sp('binary', '["abc\\n", "def"]'), + sp('string', '["abc\\n", "def"]')) + msgpack_eq(0, '"abc\\ndef"', sp('string', '["abc", "def"]')) + end) + it('compares ext specials correctly', function() + msgpack_eq(1, sp('ext', '[1, ["", "ac"]]'), sp('ext', '[1, ["", "ac"]]')) + msgpack_eq(0, sp('ext', '[2, ["", "ac"]]'), sp('ext', '[1, ["", "ac"]]')) + msgpack_eq(0, sp('ext', '[1, ["", "ac"]]'), sp('ext', '[1, ["", "abc"]]')) + end) + it('compares raw maps correctly', function() + msgpack_eq(1, '{"a": 1, "b": 2}', '{"b": 2, "a": 1}') + msgpack_eq(1, '{}', '{}') + msgpack_eq(0, '{}', '{"a": 1}') + msgpack_eq(0, '{"a": 2}', '{"a": 1}') + msgpack_eq(0, '{"a": 1}', '{"b": 1}') + msgpack_eq(0, '{"a": 1}', '{"a": 1, "b": 1}') + msgpack_eq(0, '{"a": 1, "b": 1}', '{"b": 1}') + end) + it('compares map specials correctly', function() + msgpack_eq(1, mapsp(), mapsp()) + msgpack_eq(1, mapsp(sp('binary', '[""]'), '""'), + mapsp(sp('binary', '[""]'), '""')) + msgpack_eq(1, mapsp(mapsp('1', '1'), mapsp('1', '1')), + mapsp(mapsp('1', '1'), mapsp('1', '1'))) + msgpack_eq(0, mapsp(), mapsp('1', '1')) + msgpack_eq(0, mapsp(sp('binary', '["a"]'), '""'), + mapsp(sp('binary', '[""]'), '""')) + msgpack_eq(0, mapsp(sp('binary', '[""]'), '"a"'), + mapsp(sp('binary', '[""]'), '""')) + msgpack_eq(0, mapsp(sp('binary', '["a"]'), '"a"'), + mapsp(sp('binary', '[""]'), '""')) + msgpack_eq(0, mapsp(mapsp('1', '1'), mapsp('1', '1')), + mapsp(sp('binary', '[""]'), mapsp('1', '1'))) + msgpack_eq(0, mapsp(mapsp('1', '1'), mapsp('1', '1')), + mapsp(mapsp('2', '1'), mapsp('1', '1'))) + msgpack_eq(0, mapsp(mapsp('1', '1'), mapsp('1', '1')), + mapsp(mapsp('1', '2'), mapsp('1', '1'))) + msgpack_eq(0, mapsp(mapsp('1', '1'), mapsp('1', '1')), + mapsp(mapsp('1', '1'), mapsp('2', '1'))) + msgpack_eq(0, mapsp(mapsp('1', '1'), mapsp('1', '1')), + mapsp(mapsp('1', '1'), mapsp('1', '2'))) + msgpack_eq(1, mapsp(mapsp('2', '1'), mapsp('1', '1'), + mapsp('1', '1'), mapsp('1', '1')), + mapsp(mapsp('1', '1'), mapsp('1', '1'), + mapsp('2', '1'), mapsp('1', '1'))) + end) + it('compares map specials with raw maps correctly', function() + msgpack_eq(1, mapsp(), '{}') + msgpack_eq(1, mapsp(sp('string', '["1"]'), '1'), '{"1": 1}') + msgpack_eq(1, mapsp(sp('string', '["1"]'), sp('integer', '[1, 0, 0, 1]')), + '{"1": 1}') + msgpack_eq(0, mapsp(sp('integer', '[1, 0, 0, 1]'), sp('string', '["1"]')), + '{1: "1"}') + msgpack_eq(0, mapsp('"1"', sp('integer', '[1, 0, 0, 1]')), + '{"1": 1}') + msgpack_eq(0, + mapsp(sp('string', '["1"]'), '1', sp('string', '["2"]'), '2'), + '{"1": 1}') + msgpack_eq(0, mapsp(sp('string', '["1"]'), '1'), '{"1": 1, "2": 2}') + end) + it('compares raw arrays correctly', function() + msgpack_eq(1, '[]', '[]') + msgpack_eq(0, '[]', '[1]') + msgpack_eq(1, '[1]', '[1]') + msgpack_eq(1, '[[[1]]]', '[[[1]]]') + msgpack_eq(0, '[[[2]]]', '[[[1]]]') + end) + it('compares array specials correctly', function() + msgpack_eq(1, sp('array', '[]'), sp('array', '[]')) + msgpack_eq(0, sp('array', '[]'), sp('array', '[1]')) + msgpack_eq(1, sp('array', '[1]'), sp('array', '[1]')) + msgpack_eq(1, sp('array', '[[[1]]]'), sp('array', '[[[1]]]')) + msgpack_eq(0, sp('array', '[[[1]]]'), sp('array', '[[[2]]]')) + end) + it('compares array specials with raw arrays correctly', function() + msgpack_eq(1, sp('array', '[]'), '[]') + msgpack_eq(0, sp('array', '[]'), '[1]') + msgpack_eq(1, sp('array', '[1]'), '[1]') + msgpack_eq(1, sp('array', '[[[1]]]'), '[[[1]]]') + msgpack_eq(0, sp('array', '[[[1]]]'), '[[[2]]]') + end) + it('compares raw floats correctly', function() + msgpack_eq(1, '0.0', '0.0') + msgpack_eq(1, '(1.0/0.0-1.0/0.0)', '(1.0/0.0-1.0/0.0)') + -- both (1.0/0.0-1.0/0.0) and -(1.0/0.0-1.0/0.0) now return + -- str2float('nan'). ref: @18d1ba3422d + msgpack_eq(1, '(1.0/0.0-1.0/0.0)', '-(1.0/0.0-1.0/0.0)') + msgpack_eq(1, '-(1.0/0.0-1.0/0.0)', '-(1.0/0.0-1.0/0.0)') + msgpack_eq(1, '1.0/0.0', '1.0/0.0') + msgpack_eq(1, '-(1.0/0.0)', '-(1.0/0.0)') + msgpack_eq(1, '0.0', '0.0') + msgpack_eq(0, '0.0', '1.0') + msgpack_eq(0, '0.0', '(1.0/0.0-1.0/0.0)') + msgpack_eq(0, '0.0', '1.0/0.0') + msgpack_eq(0, '0.0', '-(1.0/0.0)') + msgpack_eq(0, '1.0/0.0', '-(1.0/0.0)') + msgpack_eq(0, '(1.0/0.0-1.0/0.0)', '-(1.0/0.0)') + msgpack_eq(0, '(1.0/0.0-1.0/0.0)', '1.0/0.0') + end) + it('compares float specials with raw floats correctly', function() + msgpack_eq(1, sp('float', '0.0'), '0.0') + msgpack_eq(1, sp('float', '(1.0/0.0-1.0/0.0)'), '(1.0/0.0-1.0/0.0)') + msgpack_eq(1, sp('float', '(1.0/0.0-1.0/0.0)'), '-(1.0/0.0-1.0/0.0)') + msgpack_eq(1, sp('float', '-(1.0/0.0-1.0/0.0)'), '(1.0/0.0-1.0/0.0)') + msgpack_eq(1, sp('float', '-(1.0/0.0-1.0/0.0)'), '-(1.0/0.0-1.0/0.0)') + msgpack_eq(1, sp('float', '1.0/0.0'), '1.0/0.0') + msgpack_eq(1, sp('float', '-(1.0/0.0)'), '-(1.0/0.0)') + msgpack_eq(1, sp('float', '0.0'), '0.0') + msgpack_eq(0, sp('float', '0.0'), '1.0') + msgpack_eq(0, sp('float', '0.0'), '(1.0/0.0-1.0/0.0)') + msgpack_eq(0, sp('float', '0.0'), '1.0/0.0') + msgpack_eq(0, sp('float', '0.0'), '-(1.0/0.0)') + msgpack_eq(0, sp('float', '1.0/0.0'), '-(1.0/0.0)') + msgpack_eq(0, sp('float', '(1.0/0.0-1.0/0.0)'), '-(1.0/0.0)') + msgpack_eq(0, sp('float', '(1.0/0.0-1.0/0.0)'), '1.0/0.0') + end) + it('compares float specials correctly', function() + msgpack_eq(1, sp('float', '0.0'), sp('float', '0.0')) + msgpack_eq(1, sp('float', '(1.0/0.0-1.0/0.0)'), + sp('float', '(1.0/0.0-1.0/0.0)')) + msgpack_eq(1, sp('float', '1.0/0.0'), sp('float', '1.0/0.0')) + msgpack_eq(1, sp('float', '-(1.0/0.0)'), sp('float', '-(1.0/0.0)')) + msgpack_eq(1, sp('float', '0.0'), sp('float', '0.0')) + msgpack_eq(0, sp('float', '0.0'), sp('float', '1.0')) + msgpack_eq(0, sp('float', '0.0'), sp('float', '(1.0/0.0-1.0/0.0)')) + msgpack_eq(0, sp('float', '0.0'), sp('float', '1.0/0.0')) + msgpack_eq(0, sp('float', '0.0'), sp('float', '-(1.0/0.0)')) + msgpack_eq(0, sp('float', '1.0/0.0'), sp('float', '-(1.0/0.0)')) + msgpack_eq(0, sp('float', '(1.0/0.0-1.0/0.0)'), sp('float', '-(1.0/0.0)')) + msgpack_eq(1, sp('float', '(1.0/0.0-1.0/0.0)'), + sp('float', '-(1.0/0.0-1.0/0.0)')) + msgpack_eq(1, sp('float', '-(1.0/0.0-1.0/0.0)'), + sp('float', '-(1.0/0.0-1.0/0.0)')) + msgpack_eq(0, sp('float', '(1.0/0.0-1.0/0.0)'), sp('float', '1.0/0.0')) + end) + it('compares boolean specials correctly', function() + msgpack_eq(1, sp('boolean', '1'), sp('boolean', '1')) + msgpack_eq(0, sp('boolean', '1'), sp('boolean', '0')) + end) + it('compares nil specials correctly', function() + msgpack_eq(1, sp('nil', '1'), sp('nil', '0')) + end) + it('compares nil, boolean and integer values with each other correctly', + function() + msgpack_eq(0, sp('boolean', '1'), '1') + msgpack_eq(0, sp('boolean', '1'), sp('nil', '0')) + msgpack_eq(0, sp('boolean', '1'), sp('nil', '1')) + msgpack_eq(0, sp('boolean', '0'), sp('nil', '0')) + msgpack_eq(0, sp('boolean', '0'), '0') + msgpack_eq(0, sp('boolean', '0'), sp('integer', '[1, 0, 0, 0]')) + msgpack_eq(0, sp('boolean', '0'), sp('integer', '[1, 0, 0, 1]')) + msgpack_eq(0, sp('boolean', '1'), sp('integer', '[1, 0, 0, 1]')) + msgpack_eq(0, sp('nil', '0'), sp('integer', '[1, 0, 0, 0]')) + msgpack_eq(0, sp('nil', '0'), '0') + end) + end) + + describe('function msgpack#is_int', function() + it('works', function() + eq(1, nvim_eval('msgpack#is_int(1)')) + eq(1, nvim_eval('msgpack#is_int(-1)')) + eq(1, nvim_eval(('msgpack#is_int(%s)'):format( + sp('integer', '[1, 0, 0, 1]')))) + eq(1, nvim_eval(('msgpack#is_int(%s)'):format( + sp('integer', '[-1, 0, 0, 1]')))) + eq(0, nvim_eval(('msgpack#is_int(%s)'):format( + sp('float', '0.0')))) + eq(0, nvim_eval(('msgpack#is_int(%s)'):format( + sp('boolean', '0')))) + eq(0, nvim_eval(('msgpack#is_int(%s)'):format( + sp('nil', '0')))) + eq(0, nvim_eval('msgpack#is_int("")')) + end) + end) + + describe('function msgpack#is_uint', function() + it('works', function() + eq(1, nvim_eval('msgpack#is_uint(1)')) + eq(0, nvim_eval('msgpack#is_uint(-1)')) + eq(1, nvim_eval(('msgpack#is_uint(%s)'):format( + sp('integer', '[1, 0, 0, 1]')))) + eq(0, nvim_eval(('msgpack#is_uint(%s)'):format( + sp('integer', '[-1, 0, 0, 1]')))) + eq(0, nvim_eval(('msgpack#is_uint(%s)'):format( + sp('float', '0.0')))) + eq(0, nvim_eval(('msgpack#is_uint(%s)'):format( + sp('boolean', '0')))) + eq(0, nvim_eval(('msgpack#is_uint(%s)'):format( + sp('nil', '0')))) + eq(0, nvim_eval('msgpack#is_uint("")')) + end) + end) + + describe('function msgpack#strftime', function() + it('works', function() + local epoch = os.date('%Y-%m-%dT%H:%M:%S', 0) + eq(epoch, nvim_eval('msgpack#strftime("%Y-%m-%dT%H:%M:%S", 0)')) + eq(epoch, nvim_eval( + ('msgpack#strftime("%%Y-%%m-%%dT%%H:%%M:%%S", %s)'):format(sp( + 'integer', '[1, 0, 0, 0]')))) + end) + end) + + describe('function msgpack#strptime', function() + it('works', function() + for _, v in ipairs({0, 10, 100000, 204, 1000000000}) do + local time = os.date('%Y-%m-%dT%H:%M:%S', v) + eq(v, nvim_eval('msgpack#strptime("%Y-%m-%dT%H:%M:%S", ' + .. '"' .. time .. '")')) + end + end) + end) + + describe('function msgpack#type', function() + local type_eq = function(expected, val) + eq(expected, nvim_eval(('msgpack#type(%s)'):format(val))) + end + + it('works for special dictionaries', function() + type_eq('string', sp('string', '[""]')) + type_eq('binary', sp('binary', '[""]')) + type_eq('ext', sp('ext', '[1, [""]]')) + type_eq('array', sp('array', '[]')) + type_eq('map', sp('map', '[]')) + type_eq('integer', sp('integer', '[1, 0, 0, 0]')) + type_eq('float', sp('float', '0.0')) + type_eq('boolean', sp('boolean', '0')) + type_eq('nil', sp('nil', '0')) + end) + + it('works for regular values', function() + type_eq('binary', '""') + type_eq('array', '[]') + type_eq('map', '{}') + type_eq('integer', '1') + type_eq('float', '0.0') + type_eq('float', '(1.0/0.0)') + type_eq('float', '-(1.0/0.0)') + type_eq('float', '(1.0/0.0-1.0/0.0)') + end) + end) + + describe('function msgpack#special_type', function() + local sp_type_eq = function(expected, val) + eq(expected, nvim_eval(('msgpack#special_type(%s)'):format(val))) + end + + it('works for special dictionaries', function() + sp_type_eq('string', sp('string', '[""]')) + sp_type_eq('binary', sp('binary', '[""]')) + sp_type_eq('ext', sp('ext', '[1, [""]]')) + sp_type_eq('array', sp('array', '[]')) + sp_type_eq('map', sp('map', '[]')) + sp_type_eq('integer', sp('integer', '[1, 0, 0, 0]')) + sp_type_eq('float', sp('float', '0.0')) + sp_type_eq('boolean', sp('boolean', '0')) + sp_type_eq('nil', sp('nil', '0')) + end) + + it('works for regular values', function() + sp_type_eq(0, '""') + sp_type_eq(0, '[]') + sp_type_eq(0, '{}') + sp_type_eq(0, '1') + sp_type_eq(0, '0.0') + sp_type_eq(0, '(1.0/0.0)') + sp_type_eq(0, '-(1.0/0.0)') + sp_type_eq(0, '(1.0/0.0-1.0/0.0)') + end) + end) + + describe('function msgpack#string', function() + local string_eq = function(expected, val) + eq(expected, nvim_eval(('msgpack#string(%s)'):format(val))) + end + + it('works for special dictionaries', function() + string_eq('=""', sp('string', '[""]')) + string_eq('="\\n"', sp('string', '["", ""]')) + string_eq('="ab\\0c\\nde"', sp('string', '["ab\\nc", "de"]')) + string_eq('""', sp('binary', '[""]')) + string_eq('"\\n"', sp('binary', '["", ""]')) + string_eq('"ab\\0c\\nde"', sp('binary', '["ab\\nc", "de"]')) + string_eq('+(2)""', sp('ext', '[2, [""]]')) + string_eq('+(2)"\\n"', sp('ext', '[2, ["", ""]]')) + string_eq('+(2)"ab\\0c\\nde"', sp('ext', '[2, ["ab\\nc", "de"]]')) + string_eq('[]', sp('array', '[]')) + string_eq('[[[[{}]]]]', sp('array', '[[[[{}]]]]')) + string_eq('{}', sp('map', '[]')) + string_eq('{2: 10}', sp('map', '[[2, 10]]')) + string_eq('{{1: 1}: {1: 1}, {2: 1}: {1: 1}}', + mapsp(mapsp('2', '1'), mapsp('1', '1'), + mapsp('1', '1'), mapsp('1', '1'))) + string_eq('{{1: 1}: {1: 1}, {2: 1}: {1: 1}}', + mapsp(mapsp('1', '1'), mapsp('1', '1'), + mapsp('2', '1'), mapsp('1', '1'))) + string_eq('{[1, 2, {{1: 2}: 1}]: [1, 2, {{1: 2}: 1}]}', + mapsp(('[1, 2, %s]'):format(mapsp(mapsp('1', '2'), '1')), + ('[1, 2, %s]'):format(mapsp(mapsp('1', '2'), '1')))) + string_eq('0x0000000000000000', sp('integer', '[1, 0, 0, 0]')) + string_eq('-0x0000000100000000', sp('integer', '[-1, 0, 2, 0]')) + string_eq('0x123456789abcdef0', + sp('integer', '[ 1, 0, 610839793, 448585456]')) + string_eq('-0x123456789abcdef0', + sp('integer', '[-1, 0, 610839793, 448585456]')) + string_eq('0xf23456789abcdef0', + sp('integer', '[ 1, 3, 1684581617, 448585456]')) + string_eq('-0x723456789abcdef0', + sp('integer', '[-1, 1, 1684581617, 448585456]')) + string_eq('0.0', sp('float', '0.0')) + string_eq('inf', sp('float', '(1.0/0.0)')) + string_eq('-inf', sp('float', '-(1.0/0.0)')) + string_eq('nan', sp('float', '(1.0/0.0-1.0/0.0)')) + string_eq('nan', sp('float', '-(1.0/0.0-1.0/0.0)')) + string_eq('FALSE', sp('boolean', '0')) + string_eq('TRUE', sp('boolean', '1')) + string_eq('NIL', sp('nil', '0')) + end) + + it('works for regular values', function() + string_eq('""', '""') + string_eq('"\\n"', '"\\n"') + string_eq('[]', '[]') + string_eq('[[[{}]]]', '[[[{}]]]') + string_eq('{}', '{}') + string_eq('{="2": 10}', '{2: 10}') + string_eq('{="2": [{}]}', '{2: [{}]}') + string_eq('1', '1') + string_eq('0.0', '0.0') + string_eq('inf', '(1.0/0.0)') + string_eq('-inf', '-(1.0/0.0)') + string_eq('nan', '(1.0/0.0-1.0/0.0)') + string_eq('nan', '-(1.0/0.0-1.0/0.0)') + end) + + it('works for special v: values like v:true', function() + string_eq('TRUE', 'v:true') + string_eq('FALSE', 'v:false') + string_eq('NIL', 'v:null') + end) + end) + + describe('function msgpack#deepcopy', function() + it('works for special dictionaries', function() + nvim_command('let sparr = ' .. sp('array', '[[[]]]')) + nvim_command('let spmap = ' .. mapsp('"abc"', '[[]]')) + nvim_command('let spint = ' .. sp('integer', '[1, 0, 0, 0]')) + nvim_command('let spflt = ' .. sp('float', '1.0')) + nvim_command('let spext = ' .. sp('ext', '[2, ["abc", "def"]]')) + nvim_command('let spstr = ' .. sp('string', '["abc", "def"]')) + nvim_command('let spbin = ' .. sp('binary', '["abc", "def"]')) + nvim_command('let spbln = ' .. sp('boolean', '0')) + nvim_command('let spnil = ' .. sp('nil', '0')) + + nvim_command('let sparr2 = msgpack#deepcopy(sparr)') + nvim_command('let spmap2 = msgpack#deepcopy(spmap)') + nvim_command('let spint2 = msgpack#deepcopy(spint)') + nvim_command('let spflt2 = msgpack#deepcopy(spflt)') + nvim_command('let spext2 = msgpack#deepcopy(spext)') + nvim_command('let spstr2 = msgpack#deepcopy(spstr)') + nvim_command('let spbin2 = msgpack#deepcopy(spbin)') + nvim_command('let spbln2 = msgpack#deepcopy(spbln)') + nvim_command('let spnil2 = msgpack#deepcopy(spnil)') + + eq('array', nvim_eval('msgpack#type(sparr2)')) + eq('map', nvim_eval('msgpack#type(spmap2)')) + eq('integer', nvim_eval('msgpack#type(spint2)')) + eq('float', nvim_eval('msgpack#type(spflt2)')) + eq('ext', nvim_eval('msgpack#type(spext2)')) + eq('string', nvim_eval('msgpack#type(spstr2)')) + eq('binary', nvim_eval('msgpack#type(spbin2)')) + eq('boolean', nvim_eval('msgpack#type(spbln2)')) + eq('nil', nvim_eval('msgpack#type(spnil2)')) + + nvim_command('call add(sparr._VAL, 0)') + nvim_command('call add(sparr._VAL[0], 0)') + nvim_command('call add(sparr._VAL[0][0], 0)') + nvim_command('call add(spmap._VAL, [0, 0])') + nvim_command('call add(spmap._VAL[0][1], 0)') + nvim_command('call add(spmap._VAL[0][1][0], 0)') + nvim_command('let spint._VAL[1] = 1') + nvim_command('let spflt._VAL = 0.0') + nvim_command('let spext._VAL[0] = 3') + nvim_command('let spext._VAL[1][0] = "gh"') + nvim_command('let spstr._VAL[0] = "gh"') + nvim_command('let spbin._VAL[0] = "gh"') + nvim_command('let spbln._VAL = 1') + nvim_command('let spnil._VAL = 1') + + eq({_TYPE={}, _VAL={{{}}}}, nvim_eval('sparr2')) + eq({_TYPE={}, _VAL={{'abc', {{}}}}}, nvim_eval('spmap2')) + eq({_TYPE={}, _VAL={1, 0, 0, 0}}, nvim_eval('spint2')) + eq({_TYPE={}, _VAL=1.0}, nvim_eval('spflt2')) + eq({_TYPE={}, _VAL={2, {'abc', 'def'}}}, nvim_eval('spext2')) + eq({_TYPE={}, _VAL={'abc', 'def'}}, nvim_eval('spstr2')) + eq({_TYPE={}, _VAL={'abc', 'def'}}, nvim_eval('spbin2')) + eq({_TYPE={}, _VAL=0}, nvim_eval('spbln2')) + eq({_TYPE={}, _VAL=0}, nvim_eval('spnil2')) + + nvim_command('let sparr._TYPE = []') + nvim_command('let spmap._TYPE = []') + nvim_command('let spint._TYPE = []') + nvim_command('let spflt._TYPE = []') + nvim_command('let spext._TYPE = []') + nvim_command('let spstr._TYPE = []') + nvim_command('let spbin._TYPE = []') + nvim_command('let spbln._TYPE = []') + nvim_command('let spnil._TYPE = []') + + eq('array', nvim_eval('msgpack#special_type(sparr2)')) + eq('map', nvim_eval('msgpack#special_type(spmap2)')) + eq('integer', nvim_eval('msgpack#special_type(spint2)')) + eq('float', nvim_eval('msgpack#special_type(spflt2)')) + eq('ext', nvim_eval('msgpack#special_type(spext2)')) + eq('string', nvim_eval('msgpack#special_type(spstr2)')) + eq('binary', nvim_eval('msgpack#special_type(spbin2)')) + eq('boolean', nvim_eval('msgpack#special_type(spbln2)')) + eq('nil', nvim_eval('msgpack#special_type(spnil2)')) + end) + + it('works for regular values', function() + nvim_command('let arr = [[[]]]') + nvim_command('let map = {1: {}}') + nvim_command('let int = 1') + nvim_command('let flt = 2.0') + nvim_command('let bin = "abc"') + + nvim_command('let arr2 = msgpack#deepcopy(arr)') + nvim_command('let map2 = msgpack#deepcopy(map)') + nvim_command('let int2 = msgpack#deepcopy(int)') + nvim_command('let flt2 = msgpack#deepcopy(flt)') + nvim_command('let bin2 = msgpack#deepcopy(bin)') + + eq('array', nvim_eval('msgpack#type(arr2)')) + eq('map', nvim_eval('msgpack#type(map2)')) + eq('integer', nvim_eval('msgpack#type(int2)')) + eq('float', nvim_eval('msgpack#type(flt2)')) + eq('binary', nvim_eval('msgpack#type(bin2)')) + + nvim_command('call add(arr, 0)') + nvim_command('call add(arr[0], 0)') + nvim_command('call add(arr[0][0], 0)') + nvim_command('let map.a = 1') + nvim_command('let map.1.a = 1') + nvim_command('let int = 2') + nvim_command('let flt = 3.0') + nvim_command('let bin = ""') + + eq({{{}}}, nvim_eval('arr2')) + eq({['1']={}}, nvim_eval('map2')) + eq(1, nvim_eval('int2')) + eq(2.0, nvim_eval('flt2')) + eq('abc', nvim_eval('bin2')) + end) + + it('works for special v: values like v:true', function() + meths.set_var('true', true) + meths.set_var('false', false) + meths.set_var('nil', NIL) + + nvim_command('let true2 = msgpack#deepcopy(true)') + nvim_command('let false2 = msgpack#deepcopy(false)') + nvim_command('let nil2 = msgpack#deepcopy(nil)') + + eq(true, meths.get_var('true')) + eq(false, meths.get_var('false')) + eq(NIL, meths.get_var('nil')) + end) + end) + + describe('function msgpack#eval', function() + local eval_eq = function(expected_type, expected_val, str, ...) + nvim_command(('let g:__val = msgpack#eval(\'%s\', %s)'):format(str:gsub( + '\'', '\'\''), select(1, ...) or '{}')) + eq(expected_type, nvim_eval('msgpack#type(g:__val)')) + local expected_val_full = expected_val + if (not (({float=true, integer=true})[expected_type] + and type(expected_val) ~= 'table') + and expected_type ~= 'array') then + expected_val_full = {_TYPE={}, _VAL=expected_val_full} + end + if expected_val_full == expected_val_full then + eq(expected_val_full, nvim_eval('g:__val')) + else -- NaN + local nvim_nan = tostring(nvim_eval('g:__val')) + -- -NaN is a hardware-specific detail, there's no need to test for it. + -- Accept ether 'nan' or '-nan' as the response. + ok(nvim_nan == 'nan' or nvim_nan == '-nan') + end + nvim_command('unlet g:__val') + end + + it('correctly loads binary strings', function() + eval_eq('binary', {'abcdef'}, '"abcdef"') + eval_eq('binary', {'abc', 'def'}, '"abc\\ndef"') + eval_eq('binary', {'abc\ndef'}, '"abc\\0def"') + eval_eq('binary', {'\nabc\ndef\n'}, '"\\0abc\\0def\\0"') + eval_eq('binary', {'abc\n\n\ndef'}, '"abc\\0\\0\\0def"') + eval_eq('binary', {'abc\n', '\ndef'}, '"abc\\0\\n\\0def"') + eval_eq('binary', {'abc', '', '', 'def'}, '"abc\\n\\n\\ndef"') + eval_eq('binary', {'abc', '', '', 'def', ''}, '"abc\\n\\n\\ndef\\n"') + eval_eq('binary', {'', 'abc', '', '', 'def'}, '"\\nabc\\n\\n\\ndef"') + eval_eq('binary', {''}, '""') + eval_eq('binary', {'"'}, '"\\""') + end) + + it('correctly loads strings', function() + eval_eq('string', {'abcdef'}, '="abcdef"') + eval_eq('string', {'abc', 'def'}, '="abc\\ndef"') + eval_eq('string', {'abc\ndef'}, '="abc\\0def"') + eval_eq('string', {'\nabc\ndef\n'}, '="\\0abc\\0def\\0"') + eval_eq('string', {'abc\n\n\ndef'}, '="abc\\0\\0\\0def"') + eval_eq('string', {'abc\n', '\ndef'}, '="abc\\0\\n\\0def"') + eval_eq('string', {'abc', '', '', 'def'}, '="abc\\n\\n\\ndef"') + eval_eq('string', {'abc', '', '', 'def', ''}, '="abc\\n\\n\\ndef\\n"') + eval_eq('string', {'', 'abc', '', '', 'def'}, '="\\nabc\\n\\n\\ndef"') + eval_eq('string', {''}, '=""') + eval_eq('string', {'"'}, '="\\""') + end) + + it('correctly loads ext values', function() + eval_eq('ext', {0, {'abcdef'}}, '+(0)"abcdef"') + eval_eq('ext', {0, {'abc', 'def'}}, '+(0)"abc\\ndef"') + eval_eq('ext', {0, {'abc\ndef'}}, '+(0)"abc\\0def"') + eval_eq('ext', {0, {'\nabc\ndef\n'}}, '+(0)"\\0abc\\0def\\0"') + eval_eq('ext', {0, {'abc\n\n\ndef'}}, '+(0)"abc\\0\\0\\0def"') + eval_eq('ext', {0, {'abc\n', '\ndef'}}, '+(0)"abc\\0\\n\\0def"') + eval_eq('ext', {0, {'abc', '', '', 'def'}}, '+(0)"abc\\n\\n\\ndef"') + eval_eq('ext', {0, {'abc', '', '', 'def', ''}}, + '+(0)"abc\\n\\n\\ndef\\n"') + eval_eq('ext', {0, {'', 'abc', '', '', 'def'}}, + '+(0)"\\nabc\\n\\n\\ndef"') + eval_eq('ext', {0, {''}}, '+(0)""') + eval_eq('ext', {0, {'"'}}, '+(0)"\\""') + + eval_eq('ext', {-1, {'abcdef'}}, '+(-1)"abcdef"') + eval_eq('ext', {-1, {'abc', 'def'}}, '+(-1)"abc\\ndef"') + eval_eq('ext', {-1, {'abc\ndef'}}, '+(-1)"abc\\0def"') + eval_eq('ext', {-1, {'\nabc\ndef\n'}}, '+(-1)"\\0abc\\0def\\0"') + eval_eq('ext', {-1, {'abc\n\n\ndef'}}, '+(-1)"abc\\0\\0\\0def"') + eval_eq('ext', {-1, {'abc\n', '\ndef'}}, '+(-1)"abc\\0\\n\\0def"') + eval_eq('ext', {-1, {'abc', '', '', 'def'}}, '+(-1)"abc\\n\\n\\ndef"') + eval_eq('ext', {-1, {'abc', '', '', 'def', ''}}, + '+(-1)"abc\\n\\n\\ndef\\n"') + eval_eq('ext', {-1, {'', 'abc', '', '', 'def'}}, + '+(-1)"\\nabc\\n\\n\\ndef"') + eval_eq('ext', {-1, {''}}, '+(-1)""') + eval_eq('ext', {-1, {'"'}}, '+(-1)"\\""') + end) + + it('correctly loads floats', function() + eval_eq('float', inf, 'inf') + eval_eq('float', minus_inf, '-inf') + eval_eq('float', nan, 'nan') + eval_eq('float', 1.0e10, '1.0e10') + eval_eq('float', 1.0e10, '1.0e+10') + eval_eq('float', -1.0e10, '-1.0e+10') + eval_eq('float', 1.0, '1.0') + eval_eq('float', -1.0, '-1.0') + eval_eq('float', 1.0e-10, '1.0e-10') + eval_eq('float', -1.0e-10, '-1.0e-10') + end) + + it('correctly loads integers', function() + eval_eq('integer', 10, '10') + eval_eq('integer', -10, '-10') + eval_eq('integer', { 1, 0, 610839793, 448585456}, ' 0x123456789ABCDEF0') + eval_eq('integer', {-1, 0, 610839793, 448585456}, '-0x123456789ABCDEF0') + eval_eq('integer', { 1, 3, 1684581617, 448585456}, ' 0xF23456789ABCDEF0') + eval_eq('integer', {-1, 1, 1684581617, 448585456}, '-0x723456789ABCDEF0') + eval_eq('integer', { 1, 0, 0, 0x100}, '0x100') + eval_eq('integer', {-1, 0, 0, 0x100}, '-0x100') + + eval_eq('integer', ('a'):byte(), '\'a\'') + eval_eq('integer', 0xAB, '\'«\'') + end) + + it('correctly loads constants', function() + eval_eq('boolean', 1, 'TRUE') + eval_eq('boolean', 0, 'FALSE') + eval_eq('nil', 0, 'NIL') + eval_eq('nil', 0, 'NIL', '{"NIL": 1, "nan": 2, "T": 3}') + eval_eq('float', nan, 'nan', + '{"NIL": "1", "nan": "2", "T": "3"}') + eval_eq('integer', 3, 'T', '{"NIL": "1", "nan": "2", "T": "3"}') + eval_eq('integer', {1, 0, 0, 0}, 'T', + ('{"NIL": "1", "nan": "2", "T": \'%s\'}'):format( + sp('integer', '[1, 0, 0, 0]'))) + end) + + it('correctly loads maps', function() + eval_eq('map', {}, '{}') + eval_eq('map', {{{_TYPE={}, _VAL={{1, 2}}}, {_TYPE={}, _VAL={{3, 4}}}}}, + '{{1: 2}: {3: 4}}') + eval_eq('map', {{{_TYPE={}, _VAL={{1, 2}}}, {_TYPE={}, _VAL={{3, 4}}}}, + {1, 2}}, + '{{1: 2}: {3: 4}, 1: 2}') + end) + + it('correctly loads arrays', function() + eval_eq('array', {}, '[]') + eval_eq('array', {1}, '[1]') + eval_eq('array', {{_TYPE={}, _VAL=1}}, '[TRUE]') + eval_eq('array', {{{_TYPE={}, _VAL={{1, 2}}}}, {_TYPE={}, _VAL={{3, 4}}}}, + '[[{1: 2}], {3: 4}]') + end) + + it('errors out when needed', function() + eq('empty:Parsed string is empty', + exc_exec('call msgpack#eval("", {})')) + eq('unknown:Invalid non-space character: ^', + exc_exec('call msgpack#eval("^", {})')) + eq('char-invalid:Invalid integer character literal format: \'\'', + exc_exec('call msgpack#eval("\'\'", {})')) + eq('char-invalid:Invalid integer character literal format: \'ab\'', + exc_exec('call msgpack#eval("\'ab\'", {})')) + eq('char-invalid:Invalid integer character literal format: \'', + exc_exec('call msgpack#eval("\'", {})')) + eq('"-invalid:Invalid string: "', + exc_exec('call msgpack#eval("\\"", {})')) + eq('"-invalid:Invalid string: ="', + exc_exec('call msgpack#eval("=\\"", {})')) + eq('"-invalid:Invalid string: +(0)"', + exc_exec('call msgpack#eval("+(0)\\"", {})')) + eq('0.-nodigits:Decimal dot must be followed by digit(s): .e1', + exc_exec('call msgpack#eval("0.e1", {})')) + eq('0x-long:Must have at most 16 hex digits: FEDCBA98765432100', + exc_exec('call msgpack#eval("0xFEDCBA98765432100", {})')) + eq('0x-empty:Must have number after 0x: ', + exc_exec('call msgpack#eval("0x", {})')) + eq('name-unknown:Unknown name FOO: FOO', + exc_exec('call msgpack#eval("FOO", {})')) + end) + end) +end) diff --git a/test/functional/plugin/shada_spec.lua b/test/functional/plugin/shada_spec.lua new file mode 100644 index 0000000000..aad0e366bf --- /dev/null +++ b/test/functional/plugin/shada_spec.lua @@ -0,0 +1,2821 @@ +local helpers = require('test.functional.helpers') +local eq, nvim_eval, nvim_command, nvim, exc_exec, funcs, nvim_feed, curbuf = + helpers.eq, helpers.eval, helpers.command, helpers.nvim, helpers.exc_exec, + helpers.funcs, helpers.feed, helpers.curbuf +local neq = helpers.neq + +local mpack = require('mpack') + +local plugin_helpers = require('test.functional.plugin.helpers') +local reset = plugin_helpers.reset + +local shada_helpers = require('test.functional.shada.helpers') +local get_shada_rw = shada_helpers.get_shada_rw + +local mpack_eq = function(expected, mpack_result) + local mpack_keys = {'type', 'timestamp', 'length', 'value'} + + local unpack = mpack.Unpacker() + local actual = {} + local cur, val + local i = 0 + local off = 1 + while off <= #mpack_result do + val, off = unpack(mpack_result, off) + if i % 4 == 0 then + cur = {} + actual[#actual + 1] = cur + end + local key = mpack_keys[(i % 4) + 1] + if key ~= 'length' then + if key == 'timestamp' and math.abs(val - os.time()) < 2 then + val = 'current' + end + cur[key] = val + end + i = i + 1 + end + eq(expected, actual) +end + +local wshada, _, fname = get_shada_rw('Xtest-functional-plugin-shada.shada') + +local wshada_tmp, _, fname_tmp = + get_shada_rw('Xtest-functional-plugin-shada.shada.tmp.f') + +describe('In autoload/shada.vim', function() + local epoch = os.date('%Y-%m-%dT%H:%M:%S', 0) + before_each(function() + reset() + nvim_command([[ + function ModifyVal(val) + if type(a:val) == type([]) + if len(a:val) == 2 && type(a:val[0]) == type('') && a:val[0][0] is# '!' && has_key(v:msgpack_types, a:val[0][1:]) + return {'_TYPE': v:msgpack_types[ a:val[0][1:] ], '_VAL': a:val[1]} + else + return map(copy(a:val), 'ModifyVal(v:val)') + endif + elseif type(a:val) == type({}) + let keys = sort(keys(a:val)) + let ret = {'_TYPE': v:msgpack_types.map, '_VAL': []} + for key in keys + let k = {'_TYPE': v:msgpack_types.string, '_VAL': split(key, "\n", 1)} + let v = ModifyVal(a:val[key]) + call add(ret._VAL, [k, v]) + unlet v + endfor + return ret + elseif type(a:val) == type('') + return {'_TYPE': v:msgpack_types.binary, '_VAL': split(a:val, "\n", 1)} + else + return a:val + endif + endfunction + ]]) + end) + + local sp = function(typ, val) + return ('{"_TYPE": v:msgpack_types.%s, "_VAL": %s}'):format(typ, val) + end + + describe('function shada#mpack_to_sd', function() + local mpack2sd = function(arg) + return ('shada#mpack_to_sd(%s)'):format(arg) + end + + it('works', function() + eq({}, nvim_eval(mpack2sd('[]'))) + eq({{type=1, timestamp=5, length=1, data=7}}, + nvim_eval(mpack2sd('[1, 5, 1, 7]'))) + eq({{type=1, timestamp=5, length=1, data=7}, + {type=1, timestamp=10, length=1, data=5}}, + nvim_eval(mpack2sd('[1, 5, 1, 7, 1, 10, 1, 5]'))) + eq('zero-uint:Entry 1 has type element which is zero', + exc_exec('call ' .. mpack2sd('[0, 5, 1, 7]'))) + eq('zero-uint:Entry 1 has type element which is zero', + exc_exec('call ' .. mpack2sd(('[%s, 5, 1, 7]'):format( + sp('integer', '[1, 0, 0, 0]'))))) + eq('not-uint:Entry 1 has timestamp element which is not an unsigned integer', + exc_exec('call ' .. mpack2sd('[1, -1, 1, 7]'))) + eq('not-uint:Entry 1 has length element which is not an unsigned integer', + exc_exec('call ' .. mpack2sd('[1, 1, -1, 7]'))) + eq('not-uint:Entry 1 has type element which is not an unsigned integer', + exc_exec('call ' .. mpack2sd('["", 1, -1, 7]'))) + end) + end) + + describe('function shada#sd_to_strings', function() + local sd2strings_eq = function(expected, arg) + if type(arg) == 'table' then + eq(expected, funcs['shada#sd_to_strings'](arg)) + else + eq(expected, nvim_eval(('shada#sd_to_strings(%s)'):format(arg))) + end + end + + it('works with empty input', function() + sd2strings_eq({}, '[]') + end) + + it('works with unknown items', function() + sd2strings_eq({ + 'Unknown (0x64) with timestamp ' .. epoch .. ':', + ' = 100' + }, {{type=100, timestamp=0, length=1, data=100}}) + + sd2strings_eq({ + 'Unknown (0x4000001180000006) with timestamp ' .. epoch .. ':', + ' = 100' + }, ('[{"type": %s, "timestamp": 0, "length": 1, "data": 100}]'):format( + sp('integer', '[1, 1, 35, 6]') + )) + end) + + it('works with multiple unknown items', function() + sd2strings_eq({ + 'Unknown (0x64) with timestamp ' .. epoch .. ':', + ' = 100', + 'Unknown (0x65) with timestamp ' .. epoch .. ':', + ' = 500', + }, {{type=100, timestamp=0, length=1, data=100}, + {type=101, timestamp=0, length=1, data=500}}) + end) + + it('works with header items', function() + sd2strings_eq({ + 'Header with timestamp ' .. epoch .. ':', + ' % Key______ Value', + ' + generator "test"', + }, {{type=1, timestamp=0, data={generator='test'}}}) + sd2strings_eq({ + 'Header with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + a 1', + ' + b 2', + ' + c column 3', + ' + d 4', + }, {{type=1, timestamp=0, data={a=1, b=2, c=3, d=4}}}) + sd2strings_eq({ + 'Header with timestamp ' .. epoch .. ':', + ' % Key Value', + ' + t "test"', + }, {{type=1, timestamp=0, data={t='test'}}}) + sd2strings_eq({ + 'Header with timestamp ' .. epoch .. ':', + ' # Unexpected type: array instead of map', + ' = [1, 2, 3]', + }, {{type=1, timestamp=0, data={1, 2, 3}}}) + end) + + it('processes standard keys correctly, even in header', function() + sd2strings_eq({ + 'Header with timestamp ' .. epoch .. ':', + ' % Key Description________ Value', + ' + c column 0', + ' + f file name "/tmp/foo"', + ' + l line number 10', + ' + n name \'@\'', + ' + rc contents ["abc", "def"]', + ' + rt type CHARACTERWISE', + ' + rw block width 10', + ' + sb search backward TRUE', + ' + sc smartcase value FALSE', + ' + se place cursor at end TRUE', + ' + sh v:hlsearch value TRUE', + ' + sl has line offset FALSE', + ' + sm magic value TRUE', + ' + so offset value 10', + ' + sp pattern "100"', + ' + ss is :s pattern TRUE', + ' + su is last used FALSE', + }, ([[ [{'type': 1, 'timestamp': 0, 'data': { + 'sm': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1}, + 'sc': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0}, + 'sl': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0}, + 'se': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1}, + 'sb': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1}, + 'so': 10, + 'su': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0}, + 'ss': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1}, + 'sh': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1}, + 'sp': '100', + 'rt': 0, + 'rw': 10, + 'rc': ['abc', 'def'], + 'n': 0x40, + 'l': 10, + 'c': 0, + 'f': '/tmp/foo', + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Header with timestamp ' .. epoch .. ':', + ' % Key Description____ Value', + ' # Expected integer', + ' + c column "abc"', + ' # Expected no NUL bytes', + ' + f file name "abc\\0def"', + ' # Value is negative', + ' + l line number -10', + ' # Value is negative', + ' + n name -64', + ' # Expected array value', + ' + rc contents "10"', + ' # Unexpected enum value: expected one of ' + .. '0 (CHARACTERWISE), 1 (LINEWISE), 2 (BLOCKWISE)', + ' + rt type 10', + ' # Expected boolean', + ' + sc smartcase value NIL', + ' # Expected boolean', + ' + sm magic value "TRUE"', + ' # Expected integer', + ' + so offset value "TRUE"', + ' # Expected binary string', + ' + sp pattern ="abc"', + }, ([[ [{'type': 1, 'timestamp': 0, 'data': { + 'sm': 'TRUE', + 'sc': {'_TYPE': v:msgpack_types.nil, '_VAL': 0}, + 'so': 'TRUE', + 'sp': {'_TYPE': v:msgpack_types.string, '_VAL': ["abc"]}, + 'rt': 10, + 'rc': '10', + 'n': -0x40, + 'l': -10, + 'c': 'abc', + 'f': {'_TYPE': v:msgpack_types.binary, '_VAL': ["abc\ndef"]}, + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Header with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Expected no NUL bytes', + ' + f file name "abc\\0def"', + ' # Expected array of binary strings', + ' + rc contents ["abc", ="abc"]', + ' # Expected integer', + ' + rt type "ABC"', + }, ([[ [{'type': 1, 'timestamp': 0, 'data': { + 'rt': 'ABC', + 'rc': ["abc", {'_TYPE': v:msgpack_types.string, '_VAL': ["abc"]}], + 'f': {'_TYPE': v:msgpack_types.binary, '_VAL': ["abc\ndef"]}, + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Header with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Expected no NUL bytes', + ' + rc contents ["abc", "a\\nd\\0"]', + }, ([[ [{'type': 1, 'timestamp': 0, 'data': { + 'rc': ["abc", {'_TYPE': v:msgpack_types.binary, '_VAL': ["a", "d\n"]}], + }}] ]]):gsub('\n', '')) + end) + + it('works with search pattern items', function() + sd2strings_eq({ + 'Search pattern with timestamp ' .. epoch .. ':', + ' # Unexpected type: array instead of map', + ' = [1, 2, 3]', + }, {{type=2, timestamp=0, data={1, 2, 3}}}) + sd2strings_eq({ + 'Search pattern with timestamp ' .. epoch .. ':', + ' % Key Description________ Value', + ' + sp pattern "abc"', + ' + sh v:hlsearch value FALSE', + ' + ss is :s pattern FALSE', + ' + sb search backward FALSE', + ' + sm magic value TRUE', + ' + sc smartcase value FALSE', + ' + sl has line offset FALSE', + ' + se place cursor at end FALSE', + ' + so offset value 0', + ' + su is last used TRUE', + }, ([[ [{'type': 2, 'timestamp': 0, 'data': { + 'sp': 'abc', + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Search pattern with timestamp ' .. epoch .. ':', + ' % Key Description________ Value', + ' + sp pattern "abc"', + ' + sh v:hlsearch value FALSE', + ' + ss is :s pattern FALSE', + ' + sb search backward FALSE', + ' + sm magic value TRUE', + ' + sc smartcase value FALSE', + ' + sl has line offset FALSE', + ' + se place cursor at end FALSE', + ' + so offset value 0', + ' + su is last used TRUE', + ' + sX NIL', + ' + sY NIL', + ' + sZ NIL', + }, ([[ [{'type': 2, 'timestamp': 0, 'data': { + 'sp': 'abc', + 'sZ': {'_TYPE': v:msgpack_types.nil, '_VAL': 0}, + 'sY': {'_TYPE': v:msgpack_types.nil, '_VAL': 0}, + 'sX': {'_TYPE': v:msgpack_types.nil, '_VAL': 0}, + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Search pattern with timestamp ' .. epoch .. ':', + ' % Key Description________ Value', + ' + sp pattern "abc"', + ' + sh v:hlsearch value FALSE', + ' + ss is :s pattern FALSE', + ' + sb search backward FALSE', + ' + sm magic value TRUE', + ' + sc smartcase value FALSE', + ' + sl has line offset FALSE', + ' + se place cursor at end FALSE', + ' + so offset value 0', + ' + su is last used TRUE', + }, ([[ [{'type': 2, 'timestamp': 0, 'data': { + 'sp': 'abc', + 'sh': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0}, + 'ss': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0}, + 'sb': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0}, + 'sm': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1}, + 'sc': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0}, + 'sl': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0}, + 'se': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0}, + 'so': 0, + 'su': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1}, + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Search pattern with timestamp ' .. epoch .. ':', + ' % Key Description________ Value', + ' # Required key missing: sp', + ' + sh v:hlsearch value FALSE', + ' + ss is :s pattern FALSE', + ' + sb search backward FALSE', + ' + sm magic value TRUE', + ' + sc smartcase value FALSE', + ' + sl has line offset FALSE', + ' + se place cursor at end FALSE', + ' + so offset value 0', + ' + su is last used TRUE', + }, ([[ [{'type': 2, 'timestamp': 0, 'data': { + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Search pattern with timestamp ' .. epoch .. ':', + ' % Key Description________ Value', + ' + sp pattern ""', + ' + sh v:hlsearch value TRUE', + ' + ss is :s pattern TRUE', + ' + sb search backward TRUE', + ' + sm magic value FALSE', + ' + sc smartcase value TRUE', + ' + sl has line offset TRUE', + ' + se place cursor at end TRUE', + ' + so offset value -10', + ' + su is last used FALSE', + }, ([[ [{'type': 2, 'timestamp': 0, 'data': { + 'sp': '', + 'sh': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1}, + 'ss': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1}, + 'sb': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1}, + 'sm': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0}, + 'sc': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1}, + 'sl': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1}, + 'se': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1}, + 'so': -10, + 'su': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0}, + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Search pattern with timestamp ' .. epoch .. ':', + ' % Key Description________ Value', + ' # Expected binary string', + ' + sp pattern 0', + ' # Expected boolean', + ' + sh v:hlsearch value 0', + ' # Expected boolean', + ' + ss is :s pattern 0', + ' # Expected boolean', + ' + sb search backward 0', + ' # Expected boolean', + ' + sm magic value 0', + ' # Expected boolean', + ' + sc smartcase value 0', + ' # Expected boolean', + ' + sl has line offset 0', + ' # Expected boolean', + ' + se place cursor at end 0', + ' # Expected integer', + ' + so offset value ""', + ' # Expected boolean', + ' + su is last used 0', + }, ([[ [{'type': 2, 'timestamp': 0, 'data': { + 'sp': 0, + 'sh': 0, + 'ss': 0, + 'sb': 0, + 'sm': 0, + 'sc': 0, + 'sl': 0, + 'se': 0, + 'so': '', + 'su': 0, + }}] ]]):gsub('\n', '')) + end) + + it('works with replacement string items', function() + sd2strings_eq({ + 'Replacement string with timestamp ' .. epoch .. ':', + ' # Unexpected type: map instead of array', + ' = {="a": [10]}', + }, {{type=3, timestamp=0, data={a={10}}}}) + sd2strings_eq({ + 'Replacement string with timestamp ' .. epoch .. ':', + ' @ Description__________ Value', + ' # Expected more elements in list' + }, ([[ [{'type': 3, 'timestamp': 0, 'data': [ + ]}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Replacement string with timestamp ' .. epoch .. ':', + ' @ Description__________ Value', + ' # Expected binary string', + ' - :s replacement string 0', + }, ([[ [{'type': 3, 'timestamp': 0, 'data': [ + 0, + ]}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Replacement string with timestamp ' .. epoch .. ':', + ' @ Description__________ Value', + ' # Expected no NUL bytes', + ' - :s replacement string "abc\\0def"', + }, ([[ [{'type': 3, 'timestamp': 0, 'data': [ + {'_TYPE': v:msgpack_types.binary, '_VAL': ["abc\ndef"]}, + ]}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Replacement string with timestamp ' .. epoch .. ':', + ' @ Description__________ Value', + ' - :s replacement string "abc\\ndef"', + }, ([[ [{'type': 3, 'timestamp': 0, 'data': [ + {'_TYPE': v:msgpack_types.binary, '_VAL': ["abc", "def"]}, + ]}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Replacement string with timestamp ' .. epoch .. ':', + ' @ Description__________ Value', + ' - :s replacement string "abc\\ndef"', + ' - 0', + }, ([[ [{'type': 3, 'timestamp': 0, 'data': [ + {'_TYPE': v:msgpack_types.binary, '_VAL': ["abc", "def"]}, + 0, + ]}] ]]):gsub('\n', '')) + end) + + it('works with history entry items', function() + sd2strings_eq({ + 'History entry with timestamp ' .. epoch .. ':', + ' # Unexpected type: map instead of array', + ' = {="a": [10]}', + }, {{type=4, timestamp=0, data={a={10}}}}) + sd2strings_eq({ + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' # Expected more elements in list' + }, ([[ [{'type': 4, 'timestamp': 0, 'data': [ + ]}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' # Expected integer', + ' - history type ""', + ' # Expected more elements in list' + }, ([[ [{'type': 4, 'timestamp': 0, 'data': [ + '', + ]}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' # Unexpected enum value: expected one of 0 (CMD), 1 (SEARCH), ' + .. '2 (EXPR), 3 (INPUT), 4 (DEBUG)', + ' - history type 5', + ' - contents ""', + }, ([[ [{'type': 4, 'timestamp': 0, 'data': [ + 5, + '' + ]}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' # Unexpected enum value: expected one of 0 (CMD), 1 (SEARCH), ' + .. '2 (EXPR), 3 (INPUT), 4 (DEBUG)', + ' - history type 5', + ' - contents ""', + ' - 32', + }, ([[ [{'type': 4, 'timestamp': 0, 'data': [ + 5, + '', + 0x20 + ]}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type CMD', + ' - contents ""', + ' - 32', + }, ([[ [{'type': 4, 'timestamp': 0, 'data': [ + 0, + '', + 0x20 + ]}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type SEARCH', + ' - contents ""', + ' - separator \' \'', + }, ([[ [{'type': 4, 'timestamp': 0, 'data': [ + 1, + '', + 0x20 + ]}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type SEARCH', + ' - contents ""', + ' # Expected more elements in list', + }, ([[ [{'type': 4, 'timestamp': 0, 'data': [ + 1, + '', + ]}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type EXPR', + ' - contents ""', + }, ([[ [{'type': 4, 'timestamp': 0, 'data': [ + 2, + '', + ]}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type INPUT', + ' - contents ""', + }, ([[ [{'type': 4, 'timestamp': 0, 'data': [ + 3, + '', + ]}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type DEBUG', + ' - contents ""', + }, ([[ [{'type': 4, 'timestamp': 0, 'data': [ + 4, + '', + ]}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type DEBUG', + ' # Expected binary string', + ' - contents 10', + }, ([[ [{'type': 4, 'timestamp': 0, 'data': [ + 4, + 10, + ]}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type DEBUG', + ' # Expected no NUL bytes', + ' - contents "abc\\0def"', + }, ([[ [{'type': 4, 'timestamp': 0, 'data': [ + 4, + {'_TYPE': v:msgpack_types.binary, '_VAL': ["abc\ndef"]}, + ]}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type SEARCH', + ' - contents "abc"', + ' # Expected integer', + ' - separator ""', + }, ([[ [{'type': 4, 'timestamp': 0, 'data': [ + 1, + 'abc', + '', + ]}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type SEARCH', + ' - contents "abc"', + ' # Value is negative', + ' - separator -1', + }, ([[ [{'type': 4, 'timestamp': 0, 'data': [ + 1, + 'abc', + -1, + ]}] ]]):gsub('\n', '')) + end) + + it('works with register items', function() + sd2strings_eq({ + 'Register with timestamp ' .. epoch .. ':', + ' # Unexpected type: array instead of map', + ' = [1, 2, 3]', + }, {{type=5, timestamp=0, data={1, 2, 3}}}) + sd2strings_eq({ + 'Register with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Required key missing: n', + ' # Required key missing: rc', + ' + rw block width 0', + ' + rt type CHARACTERWISE', + }, ([[ [{'type': 5, 'timestamp': 0, 'data': { + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Register with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + n name \' \'', + ' # Required key missing: rc', + ' + rw block width 0', + ' + rt type CHARACTERWISE', + }, ([[ [{'type': 5, 'timestamp': 0, 'data': { + 'n': 0x20, + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Register with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + n name \' \'', + ' + rc contents ["abc", "def"]', + ' + rw block width 0', + ' + rt type CHARACTERWISE', + }, ([[ [{'type': 5, 'timestamp': 0, 'data': { + 'n': 0x20, + 'rc': ["abc", "def"], + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Register with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + n name \' \'', + ' + rc contents @', + ' | - "abcdefghijklmnopqrstuvwxyz"', + ' | - "abcdefghijklmnopqrstuvwxyz"', + ' + rw block width 0', + ' + rt type CHARACTERWISE', + }, ([[ [{'type': 5, 'timestamp': 0, 'data': { + 'n': 0x20, + 'rc': ['abcdefghijklmnopqrstuvwxyz', 'abcdefghijklmnopqrstuvwxyz'], + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Register with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + n name \' \'', + ' + rc contents @', + ' | - "abcdefghijklmnopqrstuvwxyz"', + ' | - "abcdefghijklmnopqrstuvwxyz"', + ' + rw block width 0', + ' + rt type CHARACTERWISE', + }, ([[ [{'type': 5, 'timestamp': 0, 'data': { + 'n': 0x20, + 'rc': ['abcdefghijklmnopqrstuvwxyz', 'abcdefghijklmnopqrstuvwxyz'], + 'rw': 0, + 'rt': 0, + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Register with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + n name \' \'', + ' + rc contents @', + ' | - "abcdefghijklmnopqrstuvwxyz"', + ' | - "abcdefghijklmnopqrstuvwxyz"', + ' + rw block width 5', + ' + rt type LINEWISE', + }, ([[ [{'type': 5, 'timestamp': 0, 'data': { + 'n': 0x20, + 'rc': ['abcdefghijklmnopqrstuvwxyz', 'abcdefghijklmnopqrstuvwxyz'], + 'rw': 5, + 'rt': 1, + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Register with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + n name \' \'', + ' + rc contents @', + ' | - "abcdefghijklmnopqrstuvwxyz"', + ' | - "abcdefghijklmnopqrstuvwxyz"', + ' # Expected integer', + ' + rw block width ""', + ' + rt type BLOCKWISE', + }, ([[ [{'type': 5, 'timestamp': 0, 'data': { + 'n': 0x20, + 'rc': ['abcdefghijklmnopqrstuvwxyz', 'abcdefghijklmnopqrstuvwxyz'], + 'rw': "", + 'rt': 2, + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Register with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + n name \' \'', + ' # Expected array value', + ' + rc contents 0', + ' # Value is negative', + ' + rw block width -1', + ' # Unexpected enum value: expected one of 0 (CHARACTERWISE), ' + .. '1 (LINEWISE), 2 (BLOCKWISE)', + ' + rt type 10', + }, ([[ [{'type': 5, 'timestamp': 0, 'data': { + 'n': 0x20, + 'rc': 0, + 'rw': -1, + 'rt': 10, + }}] ]]):gsub('\n', '')) + end) + + it('works with variable items', function() + sd2strings_eq({ + 'Variable with timestamp ' .. epoch .. ':', + ' # Unexpected type: map instead of array', + ' = {="a": [10]}', + }, {{type=6, timestamp=0, data={a={10}}}}) + sd2strings_eq({ + 'Variable with timestamp ' .. epoch .. ':', + ' @ Description Value', + ' # Expected more elements in list' + }, ([[ [{'type': 6, 'timestamp': 0, 'data': [ + ]}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Variable with timestamp ' .. epoch .. ':', + ' @ Description Value', + ' # Expected binary string', + ' - name 1', + ' # Expected more elements in list', + }, ([[ [{'type': 6, 'timestamp': 0, 'data': [ + 1 + ]}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Variable with timestamp ' .. epoch .. ':', + ' @ Description Value', + ' # Expected no NUL bytes', + ' - name "\\0"', + ' # Expected more elements in list', + }, ([[ [{'type': 6, 'timestamp': 0, 'data': [ + {'_TYPE': v:msgpack_types.binary, '_VAL': ["\n"]}, + ]}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Variable with timestamp ' .. epoch .. ':', + ' @ Description Value', + ' - name "foo"', + ' # Expected more elements in list', + }, ([[ [{'type': 6, 'timestamp': 0, 'data': [ + {'_TYPE': v:msgpack_types.binary, '_VAL': ["foo"]}, + ]}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Variable with timestamp ' .. epoch .. ':', + ' @ Description Value', + ' - name "foo"', + ' - value NIL', + }, ([[ [{'type': 6, 'timestamp': 0, 'data': [ + {'_TYPE': v:msgpack_types.binary, '_VAL': ["foo"]}, + {'_TYPE': v:msgpack_types.nil, '_VAL': ["foo"]}, + ]}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Variable with timestamp ' .. epoch .. ':', + ' @ Description Value', + ' - name "foo"', + ' - value NIL', + ' - NIL', + }, ([[ [{'type': 6, 'timestamp': 0, 'data': [ + {'_TYPE': v:msgpack_types.binary, '_VAL': ["foo"]}, + {'_TYPE': v:msgpack_types.nil, '_VAL': ["foo"]}, + {'_TYPE': v:msgpack_types.nil, '_VAL': ["foo"]}, + ]}] ]]):gsub('\n', '')) + end) + + it('works with global mark items', function() + sd2strings_eq({ + 'Global mark with timestamp ' .. epoch .. ':', + ' # Unexpected type: array instead of map', + ' = [1, 2, 3]', + }, {{type=7, timestamp=0, data={1, 2, 3}}}) + sd2strings_eq({ + 'Global mark with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Required key missing: n', + ' # Required key missing: f', + ' + l line number 1', + ' + c column 0', + }, ([[ [{'type': 7, 'timestamp': 0, 'data': { + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Global mark with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Expected integer', + ' + n name "foo"', + ' # Required key missing: f', + ' + l line number 1', + ' + c column 0', + }, ([[ [{'type': 7, 'timestamp': 0, 'data': { + 'n': 'foo', + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Global mark with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Required key missing: n', + ' + f file name "foo"', + ' + l line number 1', + ' + c column 0', + }, ([[ [{'type': 7, 'timestamp': 0, 'data': { + 'f': 'foo', + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Global mark with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Value is negative', + ' + n name -10', + ' # Expected no NUL bytes', + ' + f file name "\\0"', + ' + l line number 1', + ' + c column 0', + }, ([[ [{'type': 7, 'timestamp': 0, 'data': { + 'n': -10, + 'f': {'_TYPE': v:msgpack_types.binary, '_VAL': ["\n"]}, + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Global mark with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + n name 20', + ' + f file name "foo"', + ' # Value is negative', + ' + l line number -10', + ' # Value is negative', + ' + c column -10', + }, ([[ [{'type': 7, 'timestamp': 0, 'data': { + 'n': 20, + 'f': 'foo', + 'l': -10, + 'c': -10, + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Global mark with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + n name 20', + ' + f file name "foo"', + ' # Expected integer', + ' + l line number "FOO"', + ' # Expected integer', + ' + c column "foo"', + ' + mX 10', + }, ([[ [{'type': 7, 'timestamp': 0, 'data': { + 'n': 20, + 'f': 'foo', + 'l': 'FOO', + 'c': 'foo', + 'mX': 10, + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Global mark with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + ' + f file name "foo"', + ' + l line number 2', + ' + c column 200', + ' + mX 10', + ' + mYYYYYYYYYY 10', + }, ([[ [{'type': 7, 'timestamp': 0, 'data': { + 'n': char2nr('A'), + 'f': 'foo', + 'l': 2, + 'c': 200, + 'mX': 10, + 'mYYYYYYYYYY': 10, + }}] ]]):gsub('\n', '')) + end) + + it('works with jump items', function() + sd2strings_eq({ + 'Jump with timestamp ' .. epoch .. ':', + ' # Unexpected type: array instead of map', + ' = [1, 2, 3]', + }, {{type=8, timestamp=0, data={1, 2, 3}}}) + sd2strings_eq({ + 'Jump with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Required key missing: f', + ' + l line number 1', + ' + c column 0', + }, ([[ [{'type': 8, 'timestamp': 0, 'data': { + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Jump with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Required key missing: f', + ' + l line number 1', + ' + c column 0', + ' # Expected integer', + ' + n name "foo"', + }, ([[ [{'type': 8, 'timestamp': 0, 'data': { + 'n': 'foo', + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Jump with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + f file name "foo"', + ' + l line number 1', + ' + c column 0', + }, ([[ [{'type': 8, 'timestamp': 0, 'data': { + 'f': 'foo', + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Jump with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Expected no NUL bytes', + ' + f file name "\\0"', + ' + l line number 1', + ' + c column 0', + ' # Value is negative', + ' + n name -10', + }, ([[ [{'type': 8, 'timestamp': 0, 'data': { + 'n': -10, + 'f': {'_TYPE': v:msgpack_types.binary, '_VAL': ["\n"]}, + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Jump with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + f file name "foo"', + ' # Value is negative', + ' + l line number -10', + ' # Value is negative', + ' + c column -10', + }, ([[ [{'type': 8, 'timestamp': 0, 'data': { + 'f': 'foo', + 'l': -10, + 'c': -10, + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Jump with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + f file name "foo"', + ' # Expected integer', + ' + l line number "FOO"', + ' # Expected integer', + ' + c column "foo"', + ' + mX 10', + }, ([[ [{'type': 8, 'timestamp': 0, 'data': { + 'f': 'foo', + 'l': 'FOO', + 'c': 'foo', + 'mX': 10, + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Jump with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + f file name "foo"', + ' + l line number 2', + ' + c column 200', + ' + mX 10', + ' + mYYYYYYYYYY 10', + ' + n name \' \'', + }, ([[ [{'type': 8, 'timestamp': 0, 'data': { + 'n': 0x20, + 'f': 'foo', + 'l': 2, + 'c': 200, + 'mX': 10, + 'mYYYYYYYYYY': 10, + }}] ]]):gsub('\n', '')) + end) + + it('works with buffer list items', function() + sd2strings_eq({ + 'Buffer list with timestamp ' .. epoch .. ':', + ' # Unexpected type: map instead of array', + ' = {="a": [10]}', + }, {{type=9, timestamp=0, data={a={10}}}}) + sd2strings_eq({ + 'Buffer list with timestamp ' .. epoch .. ':', + ' # Expected array of maps', + ' = [[], []]', + }, {{type=9, timestamp=0, data={{}, {}}}}) + sd2strings_eq({ + 'Buffer list with timestamp ' .. epoch .. ':', + ' # Expected array of maps', + ' = [{="a": 10}, []]', + }, {{type=9, timestamp=0, data={{a=10}, {}}}}) + sd2strings_eq({ + 'Buffer list with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Required key missing: f', + ' + l line number 1', + ' + c column 0', + ' + a 10', + }, {{type=9, timestamp=0, data={{a=10}}}}) + sd2strings_eq({ + 'Buffer list with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Required key missing: f', + ' # Expected integer', + ' + l line number "10"', + ' # Expected integer', + ' + c column "10"', + ' + a 10', + }, {{type=9, timestamp=0, data={{l='10', c='10', a=10}}}}) + sd2strings_eq({ + 'Buffer list with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Required key missing: f', + ' + l line number 10', + ' + c column 10', + ' + a 10', + }, {{type=9, timestamp=0, data={{l=10, c=10, a=10}}}}) + sd2strings_eq({ + 'Buffer list with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Required key missing: f', + ' # Value is negative', + ' + l line number -10', + ' # Value is negative', + ' + c column -10', + }, {{type=9, timestamp=0, data={{l=-10, c=-10}}}}) + sd2strings_eq({ + 'Buffer list with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + f file name "abc"', + ' + l line number 1', + ' + c column 0', + }, {{type=9, timestamp=0, data={{f='abc'}}}}) + sd2strings_eq({ + 'Buffer list with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Expected binary string', + ' + f file name 10', + ' + l line number 1', + ' + c column 0', + '', + ' % Key Description Value', + ' # Expected binary string', + ' + f file name 20', + ' + l line number 1', + ' + c column 0', + }, {{type=9, timestamp=0, data={{f=10}, {f=20}}}}) + sd2strings_eq({ + 'Buffer list with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Expected binary string', + ' + f file name 10', + ' + l line number 1', + ' + c column 0', + '', + ' % Key Description Value', + ' # Expected no NUL bytes', + ' + f file name "\\0"', + ' + l line number 1', + ' + c column 0', + }, ([[ [{'type': 9, 'timestamp': 0, 'data': [ + {'f': 10}, + {'f': {'_TYPE': v:msgpack_types.binary, '_VAL': ["\n"]}}, + ]}] ]]):gsub('\n', '')) + end) + + it('works with local mark items', function() + sd2strings_eq({ + 'Local mark with timestamp ' .. epoch .. ':', + ' # Unexpected type: array instead of map', + ' = [1, 2, 3]', + }, {{type=10, timestamp=0, data={1, 2, 3}}}) + sd2strings_eq({ + 'Local mark with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Required key missing: f', + ' + n name \'"\'', + ' + l line number 1', + ' + c column 0', + }, ([[ [{'type': 10, 'timestamp': 0, 'data': { + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Local mark with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Required key missing: f', + ' # Expected integer', + ' + n name "foo"', + ' + l line number 1', + ' + c column 0', + }, ([[ [{'type': 10, 'timestamp': 0, 'data': { + 'n': 'foo', + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Local mark with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + f file name "foo"', + ' + n name \'"\'', + ' + l line number 1', + ' + c column 0', + }, ([[ [{'type': 10, 'timestamp': 0, 'data': { + 'f': 'foo', + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Local mark with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Expected no NUL bytes', + ' + f file name "\\0"', + ' # Value is negative', + ' + n name -10', + ' + l line number 1', + ' + c column 0', + }, ([[ [{'type': 10, 'timestamp': 0, 'data': { + 'n': -10, + 'f': {'_TYPE': v:msgpack_types.binary, '_VAL': ["\n"]}, + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Local mark with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + f file name "foo"', + ' + n name 20', + ' # Value is negative', + ' + l line number -10', + ' # Value is negative', + ' + c column -10', + }, ([[ [{'type': 10, 'timestamp': 0, 'data': { + 'n': 20, + 'f': 'foo', + 'l': -10, + 'c': -10, + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Local mark with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + f file name "foo"', + ' + n name 20', + ' # Expected integer', + ' + l line number "FOO"', + ' # Expected integer', + ' + c column "foo"', + ' + mX 10', + }, ([[ [{'type': 10, 'timestamp': 0, 'data': { + 'n': 20, + 'f': 'foo', + 'l': 'FOO', + 'c': 'foo', + 'mX': 10, + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Local mark with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + f file name "foo"', + ' + n name \'a\'', + ' + l line number 2', + ' + c column 200', + ' + mX 10', + ' + mYYYYYYYYYY 10', + }, ([[ [{'type': 10, 'timestamp': 0, 'data': { + 'n': char2nr('a'), + 'f': 'foo', + 'l': 2, + 'c': 200, + 'mX': 10, + 'mYYYYYYYYYY': 10, + }}] ]]):gsub('\n', '')) + end) + + it('works with change items', function() + sd2strings_eq({ + 'Change with timestamp ' .. epoch .. ':', + ' # Unexpected type: array instead of map', + ' = [1, 2, 3]', + }, {{type=11, timestamp=0, data={1, 2, 3}}}) + sd2strings_eq({ + 'Change with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Required key missing: f', + ' + l line number 1', + ' + c column 0', + }, ([[ [{'type': 11, 'timestamp': 0, 'data': { + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Change with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Required key missing: f', + ' + l line number 1', + ' + c column 0', + ' # Expected integer', + ' + n name "foo"', + }, ([[ [{'type': 11, 'timestamp': 0, 'data': { + 'n': 'foo', + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Change with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + f file name "foo"', + ' + l line number 1', + ' + c column 0', + }, ([[ [{'type': 11, 'timestamp': 0, 'data': { + 'f': 'foo', + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Change with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Expected no NUL bytes', + ' + f file name "\\0"', + ' + l line number 1', + ' + c column 0', + ' # Value is negative', + ' + n name -10', + }, ([[ [{'type': 11, 'timestamp': 0, 'data': { + 'n': -10, + 'f': {'_TYPE': v:msgpack_types.binary, '_VAL': ["\n"]}, + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Change with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + f file name "foo"', + ' # Value is negative', + ' + l line number -10', + ' # Value is negative', + ' + c column -10', + }, ([[ [{'type': 11, 'timestamp': 0, 'data': { + 'f': 'foo', + 'l': -10, + 'c': -10, + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Change with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + f file name "foo"', + ' # Expected integer', + ' + l line number "FOO"', + ' # Expected integer', + ' + c column "foo"', + ' + mX 10', + }, ([[ [{'type': 11, 'timestamp': 0, 'data': { + 'f': 'foo', + 'l': 'FOO', + 'c': 'foo', + 'mX': 10, + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Change with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + f file name "foo"', + ' + l line number 2', + ' + c column 200', + ' + mX 10', + ' + mYYYYYYYYYY 10', + ' + n name \' \'', + }, ([[ [{'type': 11, 'timestamp': 0, 'data': { + 'n': 0x20, + 'f': 'foo', + 'l': 2, + 'c': 200, + 'mX': 10, + 'mYYYYYYYYYY': 10, + }}] ]]):gsub('\n', '')) + end) + end) + + describe('function shada#get_strings', function() + it('works', function() + eq({ + 'Header with timestamp ' .. epoch .. ':', + ' % Key Value', + }, nvim_eval('shada#get_strings(msgpackdump([1, 0, 0, {}]))')) + end) + end) + + describe('function shada#strings_to_sd', function() + + local strings2sd_eq = function(expected, input) + nvim('set_var', '__input', input) + nvim_command('let g:__actual = map(shada#strings_to_sd(g:__input), ' + .. '"filter(v:val, \\"v:key[0] isnot# \'_\' ' + .. '&& v:key isnot# \'length\'\\")")') + -- print() + if type(expected) == 'table' then + nvim('set_var', '__expected', expected) + nvim_command('let g:__expected = ModifyVal(g:__expected)') + expected = 'g:__expected' + -- print(nvim_eval('msgpack#string(g:__expected)')) + end + -- print(nvim_eval('msgpack#string(g:__actual)')) + eq(1, nvim_eval(('msgpack#equal(%s, g:__actual)'):format(expected))) + if type(expected) == 'table' then + nvim_command('unlet g:__expected') + end + nvim_command('unlet g:__input') + nvim_command('unlet g:__actual') + end + + assert:set_parameter('TableFormatLevel', 100) + + it('works with multiple items', function() + strings2sd_eq({{ + type=11, timestamp=0, data={ + f='foo', + l=2, + c=200, + mX=10, + mYYYYYYYYYY=10, + n=(' '):byte(), + } + }, { + type=1, timestamp=0, data={ + c='abc', + f={'!binary', {'abc\ndef'}}, + l=-10, + n=-64, + rc='10', + rt=10, + sc={'!nil', 0}, + sm='TRUE', + so='TRUE', + sp={'!string', {'abc'}}, + } + }}, { + 'Change with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + f file name "foo"', + ' + l line number 2', + ' + c column 200', + ' + mX 10', + ' + mYYYYYYYYYY 10', + ' + n name \' \'', + 'Header with timestamp ' .. epoch .. ':', + ' % Key Description____ Value', + ' # Expected integer', + ' + c column "abc"', + ' # Expected no NUL bytes', + ' + f file name "abc\\0def"', + ' # Value is negative', + ' + l line number -10', + ' # Value is negative', + ' + n name -64', + ' # Expected array value', + ' + rc contents "10"', + ' # Unexpected enum value: expected one of ' + .. '0 (CHARACTERWISE), 1 (LINEWISE), 2 (BLOCKWISE)', + ' + rt type 10', + ' # Expected boolean', + ' + sc smartcase value NIL', + ' # Expected boolean', + ' + sm magic value "TRUE"', + ' # Expected integer', + ' + so offset value "TRUE"', + ' # Expected binary string', + ' + sp pattern ="abc"', + }) + end) + + it('works with empty list', function() + strings2sd_eq({}, {}) + end) + + it('works with header items', function() + strings2sd_eq({{type=1, timestamp=0, data={ + generator='test', + }}}, { + 'Header with timestamp ' .. epoch .. ':', + ' % Key______ Value', + ' + generator "test"', + }) + strings2sd_eq({{type=1, timestamp=0, data={ + 1, 2, 3, + }}}, { + 'Header with timestamp ' .. epoch .. ':', + ' # Unexpected type: array instead of map', + ' = [1, 2, 3]', + }) + strings2sd_eq({{type=1, timestamp=0, data={ + a=1, b=2, c=3, d=4, + }}}, { + 'Header with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + a 1', + ' + b 2', + ' + c column 3', + ' + d 4', + }) + strings2sd_eq({{type=1, timestamp=0, data={ + c='abc', + f={'!binary', {'abc\ndef'}}, + l=-10, + n=-64, + rc='10', + rt=10, + sc={'!nil', 0}, + sm='TRUE', + so='TRUE', + sp={'!string', {'abc'}}, + }}}, { + 'Header with timestamp ' .. epoch .. ':', + ' % Key Description____ Value', + ' # Expected integer', + ' + c column "abc"', + ' # Expected no NUL bytes', + ' + f file name "abc\\0def"', + ' # Value is negative', + ' + l line number -10', + ' # Value is negative', + ' + n name -64', + ' # Expected array value', + ' + rc contents "10"', + ' # Unexpected enum value: expected one of ' + .. '0 (CHARACTERWISE), 1 (LINEWISE), 2 (BLOCKWISE)', + ' + rt type 10', + ' # Expected boolean', + ' + sc smartcase value NIL', + ' # Expected boolean', + ' + sm magic value "TRUE"', + ' # Expected integer', + ' + so offset value "TRUE"', + ' # Expected binary string', + ' + sp pattern ="abc"', + }) + end) + + it('works with search pattern items', function() + strings2sd_eq({{type=2, timestamp=0, data={ + 1, 2, 3 + }}}, { + 'Search pattern with timestamp ' .. epoch .. ':', + ' # Unexpected type: array instead of map', + ' = [1, 2, 3]', + }) + strings2sd_eq({{type=2, timestamp=0, data={ + sp='abc', + }}}, { + 'Search pattern with timestamp ' .. epoch .. ':', + ' % Key Description________ Value', + ' + sp pattern "abc"', + ' + sh v:hlsearch value FALSE', + ' + ss is :s pattern FALSE', + ' + sm magic value TRUE', + ' + sc smartcase value FALSE', + ' + sl has line offset FALSE', + ' + se place cursor at end FALSE', + ' + so offset value 0', + ' + su is last used TRUE', + }) + strings2sd_eq({{type=2, timestamp=0, data={ + sp='abc', + sX={'!nil', 0}, + sY={'!nil', 0}, + sZ={'!nil', 0}, + }}}, { + 'Search pattern with timestamp ' .. epoch .. ':', + ' % Key Description________ Value', + ' + sp pattern "abc"', + ' + sh v:hlsearch value FALSE', + ' + ss is :s pattern FALSE', + ' + sm magic value TRUE', + ' + sc smartcase value FALSE', + ' + sl has line offset FALSE', + ' + se place cursor at end FALSE', + ' + so offset value 0', + ' + su is last used TRUE', + ' + sX NIL', + ' + sY NIL', + ' + sZ NIL', + }) + strings2sd_eq({{type=2, timestamp=0, data={'!map', { + }}}}, { + 'Search pattern with timestamp ' .. epoch .. ':', + ' % Key Description________ Value', + ' # Required key missing: sp', + ' + sh v:hlsearch value FALSE', + ' + ss is :s pattern FALSE', + ' + sm magic value TRUE', + ' + sc smartcase value FALSE', + ' + sl has line offset FALSE', + ' + se place cursor at end FALSE', + ' + so offset value 0', + ' + su is last used TRUE', + }) + strings2sd_eq({{type=2, timestamp=0, data={ + sp='', + sh={'!boolean', 1}, + ss={'!boolean', 1}, + sc={'!boolean', 1}, + sl={'!boolean', 1}, + se={'!boolean', 1}, + sm={'!boolean', 0}, + su={'!boolean', 0}, + so=-10, + }}}, { + 'Search pattern with timestamp ' .. epoch .. ':', + ' % Key Description________ Value', + ' + sp pattern ""', + ' + sh v:hlsearch value TRUE', + ' + ss is :s pattern TRUE', + ' + sm magic value FALSE', + ' + sc smartcase value TRUE', + ' + sl has line offset TRUE', + ' + se place cursor at end TRUE', + ' + so offset value -10', + ' + su is last used FALSE', + }) + strings2sd_eq({{type=2, timestamp=0, data={ + sp=0, + sh=0, + ss=0, + sc=0, + sl=0, + se=0, + sm=0, + su=0, + so='', + }}}, { + 'Search pattern with timestamp ' .. epoch .. ':', + ' % Key Description________ Value', + ' # Expected binary string', + ' + sp pattern 0', + ' # Expected boolean', + ' + sh v:hlsearch value 0', + ' # Expected boolean', + ' + ss is :s pattern 0', + ' # Expected boolean', + ' + sm magic value 0', + ' # Expected boolean', + ' + sc smartcase value 0', + ' # Expected boolean', + ' + sl has line offset 0', + ' # Expected boolean', + ' + se place cursor at end 0', + ' # Expected integer', + ' + so offset value ""', + ' # Expected boolean', + ' + su is last used 0', + }) + end) + + it('works with replacement string items', function() + strings2sd_eq({{type=3, timestamp=0, data={ + a={10} + }}}, { + 'Replacement string with timestamp ' .. epoch .. ':', + ' # Unexpected type: map instead of array', + ' = {="a": [10]}', + }) + strings2sd_eq({{type=3, timestamp=0, data={ + }}}, { + 'Replacement string with timestamp ' .. epoch .. ':', + ' @ Description__________ Value', + ' # Expected more elements in list' + }) + strings2sd_eq({{type=3, timestamp=0, data={ + 0 + }}}, { + 'Replacement string with timestamp ' .. epoch .. ':', + ' @ Description__________ Value', + ' # Expected binary string', + ' - :s replacement string 0', + }) + strings2sd_eq({{type=3, timestamp=0, data={ + 'abc\ndef', 0, + }}}, { + 'Replacement string with timestamp ' .. epoch .. ':', + ' @ Description__________ Value', + ' - :s replacement string "abc\\ndef"', + ' - 0', + }) + strings2sd_eq({{type=3, timestamp=0, data={ + 'abc\ndef', + }}}, { + 'Replacement string with timestamp ' .. epoch .. ':', + ' @ Description__________ Value', + ' - :s replacement string "abc\\ndef"', + }) + end) + + it('works with history entry items', function() + strings2sd_eq({{type=4, timestamp=0, data={ + a={10}, + }}}, { + 'History entry with timestamp ' .. epoch .. ':', + ' # Unexpected type: map instead of array', + ' = {="a": [10]}', + }) + strings2sd_eq({{type=4, timestamp=0, data={ + }}}, { + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' # Expected more elements in list' + }) + strings2sd_eq({{type=4, timestamp=0, data={ + '', + }}}, { + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' # Expected integer', + ' - history type ""', + ' # Expected more elements in list' + }) + strings2sd_eq({{type=4, timestamp=0, data={ + 5, '', + }}}, { + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' # Unexpected enum value: expected one of 0 (CMD), 1 (SEARCH), ' + .. '2 (EXPR), 3 (INPUT), 4 (DEBUG)', + ' - history type 5', + ' - contents ""', + }) + strings2sd_eq({{type=4, timestamp=0, data={ + 5, '', 32, + }}}, { + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' # Unexpected enum value: expected one of 0 (CMD), 1 (SEARCH), ' + .. '2 (EXPR), 3 (INPUT), 4 (DEBUG)', + ' - history type 5', + ' - contents ""', + ' - 32', + }) + strings2sd_eq({{type=4, timestamp=0, data={ + 0, '', 32, + }}}, { + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type CMD', + ' - contents ""', + ' - 32', + }) + strings2sd_eq({{type=4, timestamp=0, data={ + 1, '', 32, + }}}, { + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type SEARCH', + ' - contents ""', + ' - separator \' \'', + }) + strings2sd_eq({{type=4, timestamp=0, data={ + 1, '', + }}}, { + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type SEARCH', + ' - contents ""', + ' # Expected more elements in list', + }) + strings2sd_eq({{type=4, timestamp=0, data={ + 2, '', + }}}, { + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type EXPR', + ' - contents ""', + }) + strings2sd_eq({{type=4, timestamp=0, data={ + 3, '' + }}}, { + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type INPUT', + ' - contents ""', + }) + strings2sd_eq({{type=4, timestamp=0, data={ + 4, '' + }}}, { + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type DEBUG', + ' - contents ""', + }) + end) + + it('works with register items', function() + strings2sd_eq({{type=5, timestamp=0, data={ + 1, 2, 3 + }}}, { + 'Register with timestamp ' .. epoch .. ':', + ' # Unexpected type: array instead of map', + ' = [1, 2, 3]', + }) + strings2sd_eq({{type=5, timestamp=0, data={'!map', { + }}}}, { + 'Register with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Required key missing: n', + ' # Required key missing: rc', + ' + rw block width 0', + ' + rt type CHARACTERWISE', + }) + strings2sd_eq({{type=5, timestamp=0, data={ + n=(' '):byte() + }}}, { + 'Register with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + n name \' \'', + ' # Required key missing: rc', + ' + rw block width 0', + ' + rt type CHARACTERWISE', + }) + strings2sd_eq({{type=5, timestamp=0, data={ + n=(' '):byte(), rc={'abc', 'def'} + }}}, { + 'Register with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + n name \' \'', + ' + rc contents ["abc", "def"]', + ' + rw block width 0', + ' + rt type CHARACTERWISE', + }) + strings2sd_eq({{type=5, timestamp=0, data={ + n=(' '):byte(), + rc={'abcdefghijklmnopqrstuvwxyz', 'abcdefghijklmnopqrstuvwxyz'}, + }}}, { + 'Register with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + n name \' \'', + ' + rc contents @', + ' | - "abcdefghijklmnopqrstuvwxyz"', + ' | - "abcdefghijklmnopqrstuvwxyz"', + ' + rw block width 0', + ' + rt type CHARACTERWISE', + }) + strings2sd_eq({{type=5, timestamp=0, data={ + n=(' '):byte(), + rc={'abcdefghijklmnopqrstuvwxyz', 'abcdefghijklmnopqrstuvwxyz'}, + rw=5, + rt=1, + }}}, { + 'Register with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + n name \' \'', + ' + rc contents @', + ' | - "abcdefghijklmnopqrstuvwxyz"', + ' | - "abcdefghijklmnopqrstuvwxyz"', + ' + rw block width 5', + ' + rt type LINEWISE', + }) + strings2sd_eq({{type=5, timestamp=0, data={ + n=(' '):byte(), + rc={'abcdefghijklmnopqrstuvwxyz', 'abcdefghijklmnopqrstuvwxyz'}, + rw=5, + rt=2, + }}}, { + 'Register with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + n name \' \'', + ' + rc contents @', + ' | - "abcdefghijklmnopqrstuvwxyz"', + ' | - "abcdefghijklmnopqrstuvwxyz"', + ' + rw block width 5', + ' + rt type BLOCKWISE', + }) + strings2sd_eq({{type=5, timestamp=0, data={ + n=(' '):byte(), + rc=0, + rw=-1, + rt=10, + }}}, { + 'Register with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + n name \' \'', + ' # Expected array value', + ' + rc contents 0', + ' # Value is negative', + ' + rw block width -1', + ' # Unexpected enum value: expected one of 0 (CHARACTERWISE), ' + .. '1 (LINEWISE), 2 (BLOCKWISE)', + ' + rt type 10', + }) + end) + + it('works with variable items', function() + strings2sd_eq({{type=6, timestamp=0, data={ + a={10} + }}}, { + 'Variable with timestamp ' .. epoch .. ':', + ' # Unexpected type: map instead of array', + ' = {="a": [10]}', + }) + strings2sd_eq({{type=6, timestamp=0, data={ + }}}, { + 'Variable with timestamp ' .. epoch .. ':', + ' @ Description Value', + ' # Expected more elements in list' + }) + strings2sd_eq({{type=6, timestamp=0, data={ + 'foo', + }}}, { + 'Variable with timestamp ' .. epoch .. ':', + ' @ Description Value', + ' - name "foo"', + ' # Expected more elements in list', + }) + strings2sd_eq({{type=6, timestamp=0, data={ + 'foo', {'!nil', 0}, + }}}, { + 'Variable with timestamp ' .. epoch .. ':', + ' @ Description Value', + ' - name "foo"', + ' - value NIL', + }) + strings2sd_eq({{type=6, timestamp=0, data={ + 'foo', {'!nil', 0}, {'!nil', 0} + }}}, { + 'Variable with timestamp ' .. epoch .. ':', + ' @ Description Value', + ' - name "foo"', + ' - value NIL', + ' - NIL', + }) + end) + + it('works with global mark items', function() + strings2sd_eq({{type=7, timestamp=0, data={ + 1, 2, 3 + }}}, { + 'Global mark with timestamp ' .. epoch .. ':', + ' # Unexpected type: array instead of map', + ' = [1, 2, 3]', + }) + strings2sd_eq({{type=7, timestamp=0, data={ + n=('A'):byte(), f='foo', l=2, c=200, mX=10, mYYYYYYYYYY=10, + }}}, { + 'Global mark with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + ' + f file name "foo"', + ' + l line number 2', + ' + c column 200', + ' + mX 10', + ' + mYYYYYYYYYY 10', + }) + end) + + it('works with jump items', function() + strings2sd_eq({{type=8, timestamp=0, data={ + 1, 2, 3 + }}}, { + 'Jump with timestamp ' .. epoch .. ':', + ' # Unexpected type: array instead of map', + ' = [1, 2, 3]', + }) + strings2sd_eq({{type=8, timestamp=0, data={ + n=('A'):byte(), f='foo', l=2, c=200, mX=10, mYYYYYYYYYY=10, + }}}, { + 'Jump with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + ' + f file name "foo"', + ' + l line number 2', + ' + c column 200', + ' + mX 10', + ' + mYYYYYYYYYY 10', + }) + end) + + it('works with buffer list items', function() + strings2sd_eq({{type=9, timestamp=0, data={ + a={10} + }}}, { + 'Buffer list with timestamp ' .. epoch .. ':', + ' # Unexpected type: map instead of array', + ' = {="a": [10]}', + }) + strings2sd_eq({{type=9, timestamp=0, data={ + {a=10}, {} + }}}, { + 'Buffer list with timestamp ' .. epoch .. ':', + ' # Expected array of maps', + ' = [{="a": 10}, []]', + }) + strings2sd_eq({{type=9, timestamp=0, data={ + {a=10}, + }}}, { + 'Buffer list with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Required key missing: f', + ' + l line number 1', + ' + c column 0', + ' + a 10', + }) + strings2sd_eq({{type=9, timestamp=0, data={ + {l='10', c='10', a=10}, + }}}, { + 'Buffer list with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Required key missing: f', + ' # Expected integer', + ' + l line number "10"', + ' # Expected integer', + ' + c column "10"', + ' + a 10', + }) + strings2sd_eq({{type=9, timestamp=0, data={ + {l=10, c=10, a=10}, + }}}, { + 'Buffer list with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Required key missing: f', + ' + l line number 10', + ' + c column 10', + ' + a 10', + }) + strings2sd_eq({{type=9, timestamp=0, data={ + {l=-10, c=-10}, + }}}, { + 'Buffer list with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Required key missing: f', + ' # Value is negative', + ' + l line number -10', + ' # Value is negative', + ' + c column -10', + }) + strings2sd_eq({{type=9, timestamp=0, data={ + {f='abc'}, + }}}, { + 'Buffer list with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + f file name "abc"', + ' + l line number 1', + ' + c column 0', + }) + strings2sd_eq({{type=9, timestamp=0, data={ + {f=10}, {f=20}, + }}}, { + 'Buffer list with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Expected binary string', + ' + f file name 10', + ' + l line number 1', + ' + c column 0', + '', + ' % Key Description Value', + ' # Expected binary string', + ' + f file name 20', + ' + l line number 1', + ' + c column 0', + }) + strings2sd_eq({{type=9, timestamp=0, data={ + {f=10}, {f={'!binary', {'\n'}}}, + }}}, { + 'Buffer list with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Expected binary string', + ' + f file name 10', + ' + l line number 1', + ' + c column 0', + '', + ' % Key Description Value', + ' # Expected no NUL bytes', + ' + f file name "\\0"', + ' + l line number 1', + ' + c column 0', + }) + end) + + it('works with local mark items', function() + strings2sd_eq({{type=10, timestamp=0, data={ + 1, 2, 3 + }}}, { + 'Local mark with timestamp ' .. epoch .. ':', + ' # Unexpected type: array instead of map', + ' = [1, 2, 3]', + }) + strings2sd_eq({{type=10, timestamp=0, data={ + n=('A'):byte(), f='foo', l=2, c=200, mX=10, mYYYYYYYYYY=10, + }}}, { + 'Local mark with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + ' + f file name "foo"', + ' + l line number 2', + ' + c column 200', + ' + mX 10', + ' + mYYYYYYYYYY 10', + }) + end) + + it('works with change items', function() + strings2sd_eq({{type=11, timestamp=0, data={ + 1, 2, 3 + }}}, { + 'Change with timestamp ' .. epoch .. ':', + ' # Unexpected type: array instead of map', + ' = [1, 2, 3]', + }) + strings2sd_eq({{type=11, timestamp=0, data={ + n=('A'):byte(), f='foo', l=2, c=200, mX=10, mYYYYYYYYYY=10, + }}}, { + 'Change with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + ' + f file name "foo"', + ' + l line number 2', + ' + c column 200', + ' + mX 10', + ' + mYYYYYYYYYY 10', + }) + end) + end) + + describe('function shada#get_binstrings', function() + local getbstrings_eq = function(expected, input) + local result = funcs['shada#get_binstrings'](input) + for i, s in ipairs(result) do + result[i] = s:gsub('\n', '\0') + end + local mpack_result = table.concat(result, '\n') + return mpack_eq(expected, mpack_result) + end + + it('works', function() + getbstrings_eq({{timestamp='current', type=1, value={ + generator='shada.vim', + version=704, + }}}, {}) + getbstrings_eq({ + {timestamp='current', type=1, value={ + generator='shada.vim', version=704 + }}, + {timestamp=0, type=1, value={generator='test'}} + }, { + 'Header with timestamp ' .. epoch .. ':', + ' % Key______ Value', + ' + generator "test"', + }) + nvim('set_var', 'shada#add_own_header', 1) + getbstrings_eq({{timestamp='current', type=1, value={ + generator='shada.vim', + version=704, + }}}, {}) + getbstrings_eq({ + {timestamp='current', type=1, value={ + generator='shada.vim', version=704 + }}, + {timestamp=0, type=1, value={generator='test'}} + }, { + 'Header with timestamp ' .. epoch .. ':', + ' % Key______ Value', + ' + generator "test"', + }) + nvim('set_var', 'shada#add_own_header', 0) + getbstrings_eq({}, {}) + getbstrings_eq({{timestamp=0, type=1, value={generator='test'}}}, { + 'Header with timestamp ' .. epoch .. ':', + ' % Key______ Value', + ' + generator "test"', + }) + nvim('set_var', 'shada#keep_old_header', 0) + getbstrings_eq({}, { + 'Header with timestamp ' .. epoch .. ':', + ' % Key______ Value', + ' + generator "test"', + }) + getbstrings_eq({ + {type=3, timestamp=0, value={'abc\ndef'}}, + {type=3, timestamp=0, value={'abc\ndef'}}, + {type=3, timestamp=0, value={'abc\ndef'}}, + }, { + 'Replacement string with timestamp ' .. epoch .. ':', + ' @ Description__________ Value', + ' - :s replacement string "abc\\ndef"', + 'Replacement string with timestamp ' .. epoch .. ':', + ' @ Description__________ Value', + ' - :s replacement string "abc\\ndef"', + 'Replacement string with timestamp ' .. epoch .. ':', + ' @ Description__________ Value', + ' - :s replacement string "abc\\ndef"', + }) + end) + end) +end) + +describe('In plugin/shada.vim', function() + local epoch = os.date('%Y-%m-%dT%H:%M:%S', 0) + before_each(function() + reset() + os.remove(fname) + os.remove(fname .. '.tst') + os.remove(fname_tmp) + end) + + teardown(function() + os.remove(fname) + os.remove(fname_tmp) + end) + + local shada_eq = function(expected, fname_) + local fd = io.open(fname_) + local mpack_result = fd:read('*a') + fd:close() + mpack_eq(expected, mpack_result) + end + + describe('event BufReadCmd', function() + it('works', function() + wshada('\004\000\009\147\000\196\002ab\196\001a') + wshada_tmp('\004\000\009\147\000\196\002ab\196\001b') + nvim_command('edit ' .. fname) + eq({ + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type CMD', + ' - contents "ab"', + ' - "a"', + }, nvim_eval('getline(1, "$")')) + eq(false, curbuf('get_option', 'modified')) + eq('shada', curbuf('get_option', 'filetype')) + nvim_command('edit ' .. fname_tmp) + eq({ + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type CMD', + ' - contents "ab"', + ' - "b"', + }, nvim_eval('getline(1, "$")')) + eq(false, curbuf('get_option', 'modified')) + eq('shada', curbuf('get_option', 'filetype')) + eq('++opt not supported', exc_exec('edit ++enc=latin1 ' .. fname)) + neq({ + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type CMD', + ' - contents "ab"', + ' - "a"', + }, nvim_eval('getline(1, "$")')) + neq(true, curbuf('get_option', 'modified')) + end) + end) + + describe('event FileReadCmd', function() + it('works', function() + wshada('\004\000\009\147\000\196\002ab\196\001a') + wshada_tmp('\004\000\009\147\000\196\002ab\196\001b') + nvim_command('$read ' .. fname) + eq({ + '', + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type CMD', + ' - contents "ab"', + ' - "a"', + }, nvim_eval('getline(1, "$")')) + eq(true, curbuf('get_option', 'modified')) + neq('shada', curbuf('get_option', 'filetype')) + nvim_command('1,$read ' .. fname_tmp) + eq({ + '', + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type CMD', + ' - contents "ab"', + ' - "a"', + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type CMD', + ' - contents "ab"', + ' - "b"', + }, nvim_eval('getline(1, "$")')) + eq(true, curbuf('get_option', 'modified')) + neq('shada', curbuf('get_option', 'filetype')) + curbuf('set_option', 'modified', false) + eq('++opt not supported', exc_exec('$read ++enc=latin1 ' .. fname)) + eq({ + '', + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type CMD', + ' - contents "ab"', + ' - "a"', + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type CMD', + ' - contents "ab"', + ' - "b"', + }, nvim_eval('getline(1, "$")')) + neq(true, curbuf('get_option', 'modified')) + end) + end) + + describe('event BufWriteCmd', function() + it('works', function() + nvim('set_var', 'shada#add_own_header', 0) + curbuf('set_lines', 0, 1, true, { + 'Jump with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + ' + f file name ["foo"]', + ' + l line number 2', + ' + c column -200', + 'Jump with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + ' + f file name ["foo"]', + ' + l line number 2', + ' + c column -200', + }) + nvim_command('w ' .. fname .. '.tst') + nvim_command('w ' .. fname) + nvim_command('w ' .. fname_tmp) + eq('++opt not supported', exc_exec('w! ++enc=latin1 ' .. fname)) + eq(table.concat({ + 'Jump with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + ' + f file name ["foo"]', + ' + l line number 2', + ' + c column -200', + 'Jump with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + ' + f file name ["foo"]', + ' + l line number 2', + ' + c column -200', + }, '\n') .. '\n', io.open(fname .. '.tst'):read('*a')) + shada_eq({{ + timestamp=0, + type=8, + value={c=-200, f={'foo'}, l=2, n=('A'):byte()}, + }, { + timestamp=0, + type=8, + value={c=-200, f={'foo'}, l=2, n=('A'):byte()}, + }}, fname) + shada_eq({{ + timestamp=0, + type=8, + value={c=-200, f={'foo'}, l=2, n=('A'):byte()}, + }, { + timestamp=0, + type=8, + value={c=-200, f={'foo'}, l=2, n=('A'):byte()}, + }}, fname_tmp) + end) + end) + + describe('event FileWriteCmd', function() + it('works', function() + nvim('set_var', 'shada#add_own_header', 0) + curbuf('set_lines', 0, 1, true, { + 'Jump with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + ' + f file name ["foo"]', + ' + l line number 2', + ' + c column -200', + 'Jump with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + ' + f file name ["foo"]', + ' + l line number 2', + ' + c column -200', + }) + nvim_command('1,3w ' .. fname .. '.tst') + nvim_command('1,3w ' .. fname) + nvim_command('1,3w ' .. fname_tmp) + eq('++opt not supported', exc_exec('1,3w! ++enc=latin1 ' .. fname)) + eq(table.concat({ + 'Jump with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + }, '\n') .. '\n', io.open(fname .. '.tst'):read('*a')) + shada_eq({{ + timestamp=0, + type=8, + value={n=('A'):byte()}, + }}, fname) + shada_eq({{ + timestamp=0, + type=8, + value={n=('A'):byte()}, + }}, fname_tmp) + end) + end) + + describe('event FileAppendCmd', function() + it('works', function() + nvim('set_var', 'shada#add_own_header', 0) + curbuf('set_lines', 0, 1, true, { + 'Jump with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + ' + f file name ["foo"]', + ' + l line number 2', + ' + c column -200', + 'Jump with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + ' + f file name ["foo"]', + ' + l line number 2', + ' + c column -200', + }) + funcs.writefile({''}, fname .. '.tst', 'b') + funcs.writefile({''}, fname, 'b') + funcs.writefile({''}, fname_tmp, 'b') + nvim_command('1,3w >> ' .. fname .. '.tst') + nvim_command('1,3w >> ' .. fname) + nvim_command('1,3w >> ' .. fname_tmp) + nvim_command('w >> ' .. fname .. '.tst') + nvim_command('w >> ' .. fname) + nvim_command('w >> ' .. fname_tmp) + eq('++opt not supported', exc_exec('1,3w! ++enc=latin1 >> ' .. fname)) + eq(table.concat({ + 'Jump with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + 'Jump with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + ' + f file name ["foo"]', + ' + l line number 2', + ' + c column -200', + 'Jump with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + ' + f file name ["foo"]', + ' + l line number 2', + ' + c column -200', + }, '\n') .. '\n', io.open(fname .. '.tst'):read('*a')) + shada_eq({{ + timestamp=0, + type=8, + value={n=('A'):byte()}, + }, { + timestamp=0, + type=8, + value={c=-200, f={'foo'}, l=2, n=('A'):byte()}, + }, { + timestamp=0, + type=8, + value={c=-200, f={'foo'}, l=2, n=('A'):byte()}, + }}, fname) + shada_eq({{ + timestamp=0, + type=8, + value={n=('A'):byte()}, + }, { + timestamp=0, + type=8, + value={c=-200, f={'foo'}, l=2, n=('A'):byte()}, + }, { + timestamp=0, + type=8, + value={c=-200, f={'foo'}, l=2, n=('A'):byte()}, + }}, fname_tmp) + end) + end) + + describe('event SourceCmd', function() + before_each(function() + reset(fname) + end) + it('works', function() + wshada('\004\000\006\146\000\196\002ab') + wshada_tmp('\004\001\006\146\000\196\002bc') + eq(0, exc_exec('source ' .. fname)) + eq(0, exc_exec('source ' .. fname_tmp)) + eq('bc', funcs.histget(':', -1)) + eq('ab', funcs.histget(':', -2)) + end) + end) +end) + +describe('ftplugin/shada.vim', function() + local epoch = os.date('%Y-%m-%dT%H:%M:%S', 0) + before_each(reset) + + it('sets indentexpr correctly', function() + nvim_command('filetype plugin indent on') + nvim_command('setlocal filetype=shada') + funcs.setline(1, { + 'Jump with timestamp ' .. epoch .. ':', + '% Key________ Description Value', + '+ n name \'A\'', + '+ f file name "foo"', + '+ l line number 2', + '+ c column 200', + '+ mX 10', + '+ mYYYYYYYYYY 10', + 'Register with timestamp ' .. epoch .. ':', + '% Key Description Value', + '+ n name \' \'', + '+ rc contents @', + '| - "abcdefghijklmnopqrstuvwxyz"', + '| - "abcdefghijklmnopqrstuvwxyz"', + '+ rw block width 0', + '+ rt type CHARACTERWISE', + 'Replacement string with timestamp ' .. epoch .. ':', + ' @ Description__________ Value', + ' - :s replacement string "abc\\ndef"', + ' Buffer list with timestamp ' .. epoch .. ':', + ' # Expected array of maps', + '= [{="a": 10}, []]', + ' Buffer list with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Expected binary string', + '+ f file name 10', + ' + l line number 1', + ' + c column 0', + '', + ' % Key Description Value', + ' # Expected binary string', + ' + f file name 20', + '+ l line number 1', + ' + c column 0', + }) + nvim_command('normal! gg=G') + eq({ + 'Jump with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + ' + f file name "foo"', + ' + l line number 2', + ' + c column 200', + ' + mX 10', + ' + mYYYYYYYYYY 10', + 'Register with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + n name \' \'', + ' + rc contents @', + ' | - "abcdefghijklmnopqrstuvwxyz"', + ' | - "abcdefghijklmnopqrstuvwxyz"', + ' + rw block width 0', + ' + rt type CHARACTERWISE', + 'Replacement string with timestamp ' .. epoch .. ':', + ' @ Description__________ Value', + ' - :s replacement string "abc\\ndef"', + 'Buffer list with timestamp ' .. epoch .. ':', + ' # Expected array of maps', + ' = [{="a": 10}, []]', + 'Buffer list with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Expected binary string', + ' + f file name 10', + ' + l line number 1', + ' + c column 0', + '', + ' % Key Description Value', + ' # Expected binary string', + ' + f file name 20', + ' + l line number 1', + ' + c column 0', + }, funcs.getline(1, funcs.line('$'))) + end) + + it('sets options correctly', function() + nvim_command('filetype plugin indent on') + nvim_command('setlocal filetype=shada') + eq(true, curbuf('get_option', 'expandtab')) + eq(2, curbuf('get_option', 'tabstop')) + eq(2, curbuf('get_option', 'softtabstop')) + eq(2, curbuf('get_option', 'shiftwidth')) + end) + + it('sets indentkeys correctly', function() + nvim_command('filetype plugin indent on') + nvim_command('setlocal filetype=shada') + funcs.setline(1, ' Replacement with timestamp ' .. epoch) + nvim_feed('ggA:\027') + eq('Replacement with timestamp ' .. epoch .. ':', curbuf('get_line', 0)) + nvim_feed('o-\027') + eq(' -', curbuf('get_line', 1)) + nvim_feed('ggO+\027') + eq('+', curbuf('get_line', 0)) + nvim_feed('GO*\027') + eq(' *', curbuf('get_line', 2)) + nvim_feed('ggO /\027') + eq(' /', curbuf('get_line', 0)) + nvim_feed('ggOx\027') + eq('x', curbuf('get_line', 0)) + end) +end) + +describe('syntax/shada.vim', function() + local epoch = os.date('%Y-%m-%dT%H:%M:%S', 0) + before_each(reset) + + it('works', function() + nvim_command('syntax on') + nvim_command('setlocal syntax=shada') + curbuf('set_lines', 0, 1, true, { + 'Header with timestamp ' .. epoch .. ':', + ' % Key Value', + ' + t "test"', + 'Jump with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + ' + f file name ["foo"]', + ' + l line number 2', + ' + c column -200', + 'Register with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + rc contents @', + ' | - {"abcdefghijklmnopqrstuvwxyz": 1.0}', + ' + rt type CHARACTERWISE', + ' + rt type LINEWISE', + ' + rt type BLOCKWISE', + 'Replacement string with timestamp ' .. epoch .. ':', + ' @ Description__________ Value', + ' - :s replacement string CMD', + ' - :s replacement string SEARCH', + ' - :s replacement string EXPR', + ' - :s replacement string INPUT', + ' - :s replacement string DEBUG', + 'Buffer list with timestamp ' .. epoch .. ':', + ' # Expected array of maps', + ' = [{="a": +(10)"ac\\0df\\ngi\\"tt\\.", TRUE: FALSE}, [NIL, +(-10)""]]', + 'Buffer list with timestamp ' .. epoch .. ':', + ' % Key Description Value', + '', + ' % Key Description Value', + 'Header with timestamp ' .. epoch .. ':', + ' % Key Description________ Value', + ' + se place cursor at end TRUE', + }) + nvim_command([[ + function GetSyntax() + let lines = [] + for l in range(1, line('$')) + let columns = [] + let line = getline(l) + for c in range(1, col([l, '$']) - 1) + let synstack = map(synstack(l, c), 'synIDattr(v:val, "name")') + if !empty(columns) && columns[-1][0] ==# synstack + let columns[-1][1] .= line[c - 1] + else + call add(columns, [ synstack, line[c - 1] ]) + endif + endfor + call add(lines, columns) + endfor + return lines + endfunction + ]]) + local hname = function(s) return {{'ShaDaEntryHeader', 'ShaDaEntryName'}, + s} end + local h = function(s) return {{'ShaDaEntryHeader'}, s} end + local htsnum = function(s) return { + {'ShaDaEntryHeader', 'ShaDaEntryTimestamp', 'ShaDaEntryTimestampNumber'}, + s + } end + local synhtssep = function(s) + return {{'ShaDaEntryHeader', 'ShaDaEntryTimestamp'}, s} + end + local synepoch = { + year = htsnum(os.date('%Y', 0)), + month = htsnum(os.date('%m', 0)), + day = htsnum(os.date('%d', 0)), + hour = htsnum(os.date('%H', 0)), + minute = htsnum(os.date('%M', 0)), + second = htsnum(os.date('%S', 0)), + } + local msh = function(s) return {{'ShaDaEntryMapShort', + 'ShaDaEntryMapHeader'}, s} end + local mlh = function(s) return {{'ShaDaEntryMapLong', + 'ShaDaEntryMapHeader'}, s} end + local ah = function(s) return {{'ShaDaEntryArray', + 'ShaDaEntryArrayHeader'}, s} end + -- luacheck: ignore + local mses = function(s) return {{'ShaDaEntryMapShort', + 'ShaDaEntryMapShortEntryStart'}, s} end + local mles = function(s) return {{'ShaDaEntryMapLong', + 'ShaDaEntryMapLongEntryStart'}, s} end + local act = funcs.GetSyntax() + local ms = function(syn) + return { + {'ShaDaEntryMap' .. syn, 'ShaDaEntryMap' .. syn .. 'EntryStart'}, ' + ' + } + end + local as = function() + return {{'ShaDaEntryArray', 'ShaDaEntryArrayEntryStart'}, ' - '} + end + local ad = function(s) return {{'ShaDaEntryArray', + 'ShaDaEntryArrayDescription'}, s} end + local mbas = function(syn) + return { + {'ShaDaEntryMap' .. syn, 'ShaDaEntryMapBinArrayStart'}, + ' | - ' + } + end + local msk = function(s) return {{'ShaDaEntryMapShort', + 'ShaDaEntryMapShortKey'}, s} end + local mlk = function(s) return {{'ShaDaEntryMapLong', + 'ShaDaEntryMapLongKey'}, s} end + local mld = function(s) return {{'ShaDaEntryMapLong', + 'ShaDaEntryMapLongDescription'}, s} end + local c = function(s) return {{'ShaDaComment'}, s} end + local exp = { + { + hname('Header'), h(' with timestamp '), + synepoch.year, synhtssep('-'), synepoch.month, synhtssep('-'), + synepoch.day, synhtssep('T'), synepoch.hour, synhtssep(':'), + synepoch.minute, synhtssep(':'), synepoch.second, h(':'), + }, + { + msh(' % Key Value'), + }, + { + ms('Short'), msk('t '), + {{'ShaDaEntryMapShort', 'ShaDaMsgpackBinaryString', + 'ShaDaMsgpackStringQuotes'}, '"'}, + {{'ShaDaEntryMapShort', 'ShaDaMsgpackBinaryString'}, 'test'}, + {{'ShaDaEntryMapShort', 'ShaDaMsgpackStringQuotes'}, '"'}, + }, + { + hname('Jump'), h(' with timestamp '), + synepoch.year, synhtssep('-'), synepoch.month, synhtssep('-'), + synepoch.day, synhtssep('T'), synepoch.hour, synhtssep(':'), + synepoch.minute, synhtssep(':'), synepoch.second, h(':'), + }, + { + mlh(' % Key________ Description Value'), + }, + { + ms('Long'), mlk('n '), mld('name '), + {{'ShaDaEntryMapLong', 'ShaDaMsgpackCharacter'}, '\'A\''}, + }, + { + ms('Long'), mlk('f '), mld('file name '), + {{'ShaDaEntryMapLong', 'ShaDaMsgpackArray', + 'ShaDaMsgpackArrayBraces'}, '['}, + {{'ShaDaEntryMapLong', 'ShaDaMsgpackArray', 'ShaDaMsgpackBinaryString', + 'ShaDaMsgpackStringQuotes'}, '"'}, + {{'ShaDaEntryMapLong', 'ShaDaMsgpackArray', 'ShaDaMsgpackBinaryString'}, + 'foo'}, + {{'ShaDaEntryMapLong', 'ShaDaMsgpackArray', 'ShaDaMsgpackStringQuotes'}, + '"'}, + {{'ShaDaEntryMapLong', 'ShaDaMsgpackArrayBraces'}, ']'}, + }, + { + ms('Long'), mlk('l '), mld('line number '), + {{'ShaDaEntryMapLong', 'ShaDaMsgpackInteger'}, '2'}, + }, + { + ms('Long'), mlk('c '), mld('column '), + {{'ShaDaEntryMapLong', 'ShaDaMsgpackInteger'}, '-200'}, + }, + { + hname('Register'), h(' with timestamp '), + synepoch.year, synhtssep('-'), synepoch.month, synhtssep('-'), + synepoch.day, synhtssep('T'), synepoch.hour, synhtssep(':'), + synepoch.minute, synhtssep(':'), synepoch.second, h(':'), + }, + { + mlh(' % Key Description Value'), + }, + { + ms('Long'), mlk('rc '), mld('contents '), + {{'ShaDaEntryMapLong', 'ShaDaMsgpackMultilineArray'}, '@'}, + }, + { + mbas('Long'), + {{'ShaDaEntryMapLong', 'ShaDaMsgpackMap', 'ShaDaMsgpackMapBraces'}, + '{'}, + {{'ShaDaEntryMapLong', 'ShaDaMsgpackMap', 'ShaDaMsgpackBinaryString', + 'ShaDaMsgpackStringQuotes'}, '"'}, + {{'ShaDaEntryMapLong', 'ShaDaMsgpackMap', 'ShaDaMsgpackBinaryString'}, + 'abcdefghijklmnopqrstuvwxyz'}, + {{'ShaDaEntryMapLong', 'ShaDaMsgpackMap', 'ShaDaMsgpackStringQuotes'}, + '"'}, + {{'ShaDaEntryMapLong', 'ShaDaMsgpackMap', 'ShaDaMsgpackColon'}, ':'}, + {{'ShaDaEntryMapLong', 'ShaDaMsgpackMap'}, ' '}, + {{'ShaDaEntryMapLong', 'ShaDaMsgpackMap', 'ShaDaMsgpackFloat'}, '1.0'}, + {{'ShaDaEntryMapLong', 'ShaDaMsgpackMapBraces'}, '}'}, + }, + { + ms('Long'), mlk('rt '), mld('type '), + {{'ShaDaEntryMapLong', 'ShaDaMsgpackShaDaKeyword'}, 'CHARACTERWISE'}, + }, + { + ms('Long'), mlk('rt '), mld('type '), + {{'ShaDaEntryMapLong', 'ShaDaMsgpackShaDaKeyword'}, 'LINEWISE'}, + }, + { + ms('Long'), mlk('rt '), mld('type '), + {{'ShaDaEntryMapLong', 'ShaDaMsgpackShaDaKeyword'}, 'BLOCKWISE'}, + }, + { + hname('Replacement string'), h(' with timestamp '), + synepoch.year, synhtssep('-'), synepoch.month, synhtssep('-'), + synepoch.day, synhtssep('T'), synepoch.hour, synhtssep(':'), + synepoch.minute, synhtssep(':'), synepoch.second, h(':'), + }, + { + ah(' @ Description__________ Value'), + }, + { + as(), ad(':s replacement string '), + {{'ShaDaEntryArray', 'ShaDaMsgpackShaDaKeyword'}, 'CMD'}, + }, + { + as(), ad(':s replacement string '), + {{'ShaDaEntryArray', 'ShaDaMsgpackShaDaKeyword'}, 'SEARCH'}, + }, + { + as(), ad(':s replacement string '), + {{'ShaDaEntryArray', 'ShaDaMsgpackShaDaKeyword'}, 'EXPR'}, + }, + { + as(), ad(':s replacement string '), + {{'ShaDaEntryArray', 'ShaDaMsgpackShaDaKeyword'}, 'INPUT'}, + }, + { + {{'ShaDaEntryArrayEntryStart'}, ' - '}, + {{'ShaDaEntryArrayDescription'}, ':s replacement string '}, + {{'ShaDaMsgpackShaDaKeyword'}, 'DEBUG'}, + }, + { + hname('Buffer list'), h(' with timestamp '), + synepoch.year, synhtssep('-'), synepoch.month, synhtssep('-'), + synepoch.day, synhtssep('T'), synepoch.hour, synhtssep(':'), + synepoch.minute, synhtssep(':'), synepoch.second, h(':'), + }, + { + c(' # Expected array of maps'), + }, + { + {{'ShaDaEntryRawMsgpack'}, ' = '}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackArrayBraces'}, '['}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackMapBraces'}, + '{'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackString'}, '='}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackBinaryString', + 'ShaDaMsgpackStringQuotes'}, '"'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackBinaryString'}, + 'a'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackStringQuotes'}, + '"'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackColon'}, ':'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap'}, ' '}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackExt'}, '+('}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackExt', + 'ShaDaMsgpackExtType'}, '10'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackExt'}, ')'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackBinaryString', + 'ShaDaMsgpackStringQuotes'}, '"'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackBinaryString'}, + 'ac'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackBinaryString', + 'ShaDaMsgpackBinaryStringEscape'}, + '\\0'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackBinaryString'}, + 'df'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackBinaryString', + 'ShaDaMsgpackBinaryStringEscape'}, + '\\n'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackBinaryString'}, + 'gi'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackBinaryString', + 'ShaDaMsgpackBinaryStringEscape'}, + '\\"'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackBinaryString'}, + 'tt\\.'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackStringQuotes'}, + '"'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackComma'}, ','}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap'}, ' '}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackKeyword'}, + 'TRUE'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackColon'}, ':'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap'}, ' '}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackKeyword'}, + 'FALSE'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMapBraces'}, '}'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackComma'}, ','}, + {{'ShaDaMsgpackArray'}, ' '}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackArray', 'ShaDaMsgpackArrayBraces'}, + '['}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackArray', 'ShaDaMsgpackKeyword'}, + 'NIL'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackArray', 'ShaDaMsgpackComma'}, ','}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackArray'}, ' '}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackArray', 'ShaDaMsgpackExt'}, '+('}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackArray', 'ShaDaMsgpackExt', + 'ShaDaMsgpackExtType'}, '-10'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackArray', 'ShaDaMsgpackExt'}, ')'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackArray', 'ShaDaMsgpackBinaryString', + 'ShaDaMsgpackStringQuotes'}, '"'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackArray', 'ShaDaMsgpackStringQuotes'}, + '"'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackArrayBraces'}, ']'}, + {{'ShaDaMsgpackArrayBraces'}, ']'}, + }, + { + hname('Buffer list'), h(' with timestamp '), + synepoch.year, synhtssep('-'), synepoch.month, synhtssep('-'), + synepoch.day, synhtssep('T'), synepoch.hour, synhtssep(':'), + synepoch.minute, synhtssep(':'), synepoch.second, h(':'), + }, + { + mlh(' % Key Description Value'), + }, + { + }, + { + mlh(' % Key Description Value'), + }, + { + hname('Header'), h(' with timestamp '), + synepoch.year, synhtssep('-'), synepoch.month, synhtssep('-'), + synepoch.day, synhtssep('T'), synepoch.hour, synhtssep(':'), + synepoch.minute, synhtssep(':'), synepoch.second, h(':'), + }, + { + mlh(' % Key Description________ Value'), + }, + { + {{'ShaDaEntryMapLongEntryStart'}, ' + '}, + {{'ShaDaEntryMapLongKey'}, 'se '}, + {{'ShaDaEntryMapLongDescription'}, 'place cursor at end '}, + {{'ShaDaMsgpackKeyword'}, 'TRUE'}, + }, + } + eq(exp, act) + end) +end) + diff --git a/test/functional/preload.lua b/test/functional/preload.lua index 5f34f7fa6e..1971ef77cc 100644 --- a/test/functional/preload.lua +++ b/test/functional/preload.lua @@ -1,5 +1,4 @@ -- Modules loaded here will not be cleared and reloaded by Busted. -- Busted started doing this to help provide more isolation. See issue #62 -- for more information about this. -local ffi = require('ffi') local helpers = require('test.functional.helpers') diff --git a/test/functional/provider/define_spec.lua b/test/functional/provider/define_spec.lua index 9b97ed84d9..c30ad6d8c2 100644 --- a/test/functional/provider/define_spec.lua +++ b/test/functional/provider/define_spec.lua @@ -1,7 +1,7 @@ local helpers = require('test.functional.helpers') local eval, command, nvim = helpers.eval, helpers.command, helpers.nvim local eq, run, stop = helpers.eq, helpers.run, helpers.stop -local clear, feed = helpers.clear, helpers.feed +local clear = helpers.clear local function get_prefix(sync) @@ -12,8 +12,8 @@ local function get_prefix(sync) end -local function call(fn, args) - command('call '..fn..'('..args..')') +local function call(fn, arguments) + command('call '..fn..'('..arguments..')') end @@ -64,153 +64,137 @@ local function command_specs_for(fn, sync, first_arg_factory, init) args = args..', "RpcCommand"' end) - describe('without options', function() - it('ok', function() - call(fn, args..', {}') - local function on_setup() - command('RpcCommand') - end + it('without options', function() + call(fn, args..', {}') + local function on_setup() + command('RpcCommand') + end - local function handler(method) - eq('test-handler', method) - return '' - end + local function handler(method) + eq('test-handler', method) + return '' + end - runx(sync, handler, on_setup) - end) + runx(sync, handler, on_setup) end) - describe('with nargs', function() - it('ok', function() - call(fn, args..', {"nargs": "*"}') - local function on_setup() - command('RpcCommand arg1 arg2 arg3') - end + it('with nargs', function() + call(fn, args..', {"nargs": "*"}') + local function on_setup() + command('RpcCommand arg1 arg2 arg3') + end - local function handler(method, args) - eq('test-handler', method) - eq({'arg1', 'arg2', 'arg3'}, args[1]) - return '' - end + local function handler(method, arguments) + eq('test-handler', method) + eq({'arg1', 'arg2', 'arg3'}, arguments[1]) + return '' + end - runx(sync, handler, on_setup) - end) + runx(sync, handler, on_setup) end) - describe('with range', function() - it('ok', function() - call(fn,args..', {"range": ""}') - local function on_setup() - command('1,1RpcCommand') - end + it('with range', function() + call(fn,args..', {"range": ""}') + local function on_setup() + command('1,1RpcCommand') + end - local function handler(method, args) - eq('test-handler', method) - eq({1, 1}, args[1]) - return '' - end + local function handler(method, arguments) + eq('test-handler', method) + eq({1, 1}, arguments[1]) + return '' + end - runx(sync, handler, on_setup) - end) + runx(sync, handler, on_setup) end) - describe('with nargs/range', function() - it('ok', function() - call(fn, args..', {"nargs": "1", "range": ""}') - local function on_setup() - command('1,1RpcCommand arg') - end + it('with nargs/range', function() + call(fn, args..', {"nargs": "1", "range": ""}') + local function on_setup() + command('1,1RpcCommand arg') + end - local function handler(method, args) - eq('test-handler', method) - eq({'arg'}, args[1]) - eq({1, 1}, args[2]) - return '' - end + local function handler(method, arguments) + eq('test-handler', method) + eq({'arg'}, arguments[1]) + eq({1, 1}, arguments[2]) + return '' + end - runx(sync, handler, on_setup) - end) + runx(sync, handler, on_setup) end) - describe('with nargs/count', function() - it('ok', function() - call(fn, args..', {"nargs": "1", "range": "5"}') - local function on_setup() - command('5RpcCommand arg') - end + it('with nargs/count', function() + call(fn, args..', {"nargs": "1", "range": "5"}') + local function on_setup() + command('5RpcCommand arg') + end - local function handler(method, args) - eq('test-handler', method) - eq({'arg'}, args[1]) - eq(5, args[2]) - return '' - end + local function handler(method, arguments) + eq('test-handler', method) + eq({'arg'}, arguments[1]) + eq(5, arguments[2]) + return '' + end - runx(sync, handler, on_setup) - end) + runx(sync, handler, on_setup) end) - describe('with nargs/count/bang', function() - it('ok', function() - call(fn, args..', {"nargs": "1", "range": "5", "bang": ""}') - local function on_setup() - command('5RpcCommand! arg') - end + it('with nargs/count/bang', function() + call(fn, args..', {"nargs": "1", "range": "5", "bang": ""}') + local function on_setup() + command('5RpcCommand! arg') + end - local function handler(method, args) - eq('test-handler', method) - eq({'arg'}, args[1]) - eq(5, args[2]) - eq(1, args[3]) - return '' - end + local function handler(method, arguments) + eq('test-handler', method) + eq({'arg'}, arguments[1]) + eq(5, arguments[2]) + eq(1, arguments[3]) + return '' + end - runx(sync, handler, on_setup) - end) + runx(sync, handler, on_setup) end) - describe('with nargs/count/bang/register', function() - it('ok', function() - call(fn, args..', {"nargs": "1", "range": "5", "bang": "",'.. - ' "register": ""}') - local function on_setup() - command('5RpcCommand! b arg') - end + it('with nargs/count/bang/register', function() + call(fn, args..', {"nargs": "1", "range": "5", "bang": "",'.. + ' "register": ""}') + local function on_setup() + command('5RpcCommand! b arg') + end - local function handler(method, args) - eq('test-handler', method) - eq({'arg'}, args[1]) - eq(5, args[2]) - eq(1, args[3]) - eq('b', args[4]) - return '' - end + local function handler(method, arguments) + eq('test-handler', method) + eq({'arg'}, arguments[1]) + eq(5, arguments[2]) + eq(1, arguments[3]) + eq('b', arguments[4]) + return '' + end - runx(sync, handler, on_setup) - end) + runx(sync, handler, on_setup) end) - describe('with nargs/count/bang/register/eval', function() - it('ok', function() - call(fn, args..', {"nargs": "1", "range": "5", "bang": "",'.. - ' "register": "", "eval": "@<reg>"}') - local function on_setup() - command('let @b = "regb"') - command('5RpcCommand! b arg') - end + it('with nargs/count/bang/register/eval', function() + call(fn, args..', {"nargs": "1", "range": "5", "bang": "",'.. + ' "register": "", "eval": "@<reg>"}') + local function on_setup() + command('let @b = "regb"') + command('5RpcCommand! b arg') + end - local function handler(method, args) - eq('test-handler', method) - eq({'arg'}, args[1]) - eq(5, args[2]) - eq(1, args[3]) - eq('b', args[4]) - eq('regb', args[5]) - return '' - end + local function handler(method, arguments) + eq('test-handler', method) + eq({'arg'}, arguments[1]) + eq(5, arguments[2]) + eq(1, arguments[3]) + eq('b', arguments[4]) + eq('regb', arguments[5]) + return '' + end - runx(sync, handler, on_setup) - end) + runx(sync, handler, on_setup) end) end) end) @@ -236,37 +220,33 @@ local function autocmd_specs_for(fn, sync, first_arg_factory, init) args = args..', "BufEnter"' end) - describe('without options', function() - it('ok', function() - call(fn, args..', {}') - local function on_setup() - command('doautocmd BufEnter x.c') - end + it('without options', function() + call(fn, args..', {}') + local function on_setup() + command('doautocmd BufEnter x.c') + end - local function handler(method, args) - eq('test-handler', method) - return '' - end + local function handler(method) + eq('test-handler', method) + return '' + end - runx(sync, handler, on_setup) - end) + runx(sync, handler, on_setup) end) - describe('with eval', function() - it('ok', function() - call(fn, args..[[, {'eval': 'expand("<afile>")'}]]) - local function on_setup() - command('doautocmd BufEnter x.c') - end + it('with eval', function() + call(fn, args..[[, {'eval': 'expand("<afile>")'}]]) + local function on_setup() + command('doautocmd BufEnter x.c') + end - local function handler(method, args) - eq('test-handler', method) - eq('x.c', args[1]) - return '' - end + local function handler(method, arguments) + eq('test-handler', method) + eq('x.c', arguments[1]) + return '' + end - runx(sync, handler, on_setup) - end) + runx(sync, handler, on_setup) end) end) end) @@ -292,46 +272,77 @@ local function function_specs_for(fn, sync, first_arg_factory, init) args = args..', "TestFunction"' end) - describe('without options', function() - it('ok', function() - call(fn, args..', {}') - local function on_setup() - if sync then - eq('rv', eval('TestFunction(1, "a", ["b", "c"])')) - else - eq(1, eval('TestFunction(1, "a", ["b", "c"])')) - end + it('without options', function() + call(fn, args..', {}') + local function on_setup() + if sync then + eq('rv', eval('TestFunction(1, "a", ["b", "c"])')) + else + eq(1, eval('TestFunction(1, "a", ["b", "c"])')) end + end - local function handler(method, args) - eq('test-handler', method) - eq({{1, 'a', {'b', 'c'}}}, args) - return 'rv' - end + local function handler(method, arguments) + eq('test-handler', method) + eq({{1, 'a', {'b', 'c'}}}, arguments) + return 'rv' + end - runx(sync, handler, on_setup) - end) + runx(sync, handler, on_setup) end) - describe('with eval', function() - it('ok', function() - call(fn, args..[[, {'eval': '2 + 2'}]]) - local function on_setup() - if sync then - eq('rv', eval('TestFunction(1, "a", ["b", "c"])')) - else - eq(1, eval('TestFunction(1, "a", ["b", "c"])')) - end + it('with eval', function() + call(fn, args..[[, {'eval': '2 + 2'}]]) + local function on_setup() + if sync then + eq('rv', eval('TestFunction(1, "a", ["b", "c"])')) + else + eq(1, eval('TestFunction(1, "a", ["b", "c"])')) end + end - local function handler(method, args) - eq('test-handler', method) - eq({{1, 'a', {'b', 'c'}}, 4}, args) - return 'rv' - end + local function handler(method, arguments) + eq('test-handler', method) + eq({{1, 'a', {'b', 'c'}}, 4}, arguments) + return 'rv' + end + + runx(sync, handler, on_setup) + end) + + it('with range', function() + helpers.insert([[ + foo + bar + baz + zub]]) + call(fn, args..[[, {'range': ''}]]) + local function on_setup() + command('2,3call TestFunction(1, "a", ["b", "c"])') + end + + local function handler(method, arguments) + eq('test-handler', method) + eq({{1, 'a', {'b', 'c'}}, {2, 3}}, arguments) + return 'rv' + end + + runx(sync, handler, on_setup) + end) + + it('with eval/range', function() + call(fn, args..[[, {'eval': '4', 'range': ''}]]) + local function on_setup() + command('%call TestFunction(1, "a", ["b", "c"])') + end + + local function handler(method, arguments) + eq('test-handler', method) + eq({{1, 'a', {'b', 'c'}}, {1, 1}, 4}, arguments) + return 'rv' + end - runx(sync, handler, on_setup) - end) + runx(sync, handler, on_setup) end) 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/buffers_spec.lua b/test/functional/shada/buffers_spec.lua index 3666b718f0..fd4809e01a 100644 --- a/test/functional/shada/buffers_spec.lua +++ b/test/functional/shada/buffers_spec.lua @@ -1,7 +1,7 @@ -- ShaDa buffer list saving/reading support local helpers = require('test.functional.helpers') -local nvim_command, funcs, eq = - helpers.command, helpers.funcs, helpers.eq +local nvim_command, funcs, eq, curbufmeths = + helpers.command, helpers.funcs, helpers.eq, helpers.curbufmeths local shada_helpers = require('test.functional.shada.helpers') local reset, set_additional_cmd, clear = @@ -9,8 +9,8 @@ local reset, set_additional_cmd, clear = shada_helpers.clear describe('ShaDa support code', function() - testfilename = 'Xtestfile-functional-shada-buffers' - testfilename_2 = 'Xtestfile-functional-shada-buffers-2' + local testfilename = 'Xtestfile-functional-shada-buffers' + local testfilename_2 = 'Xtestfile-functional-shada-buffers-2' before_each(reset) after_each(clear) @@ -48,4 +48,43 @@ describe('ShaDa support code', function() eq(1, funcs.bufnr('$')) eq('', funcs.bufname(1)) end) + + it('does not dump unlisted buffer', function() + set_additional_cmd('set shada+=%') + reset() + nvim_command('edit ' .. testfilename) + nvim_command('edit ' .. testfilename_2) + curbufmeths.set_option('buflisted', false) + nvim_command('qall') + reset() + eq(2, funcs.bufnr('$')) + eq('', funcs.bufname(1)) + eq(testfilename, funcs.bufname(2)) + end) + + it('does not dump quickfix buffer', function() + set_additional_cmd('set shada+=%') + reset() + nvim_command('edit ' .. testfilename) + nvim_command('edit ' .. testfilename_2) + curbufmeths.set_option('buftype', 'quickfix') + nvim_command('qall') + reset() + eq(2, funcs.bufnr('$')) + eq('', funcs.bufname(1)) + eq(testfilename, funcs.bufname(2)) + end) + + it('does not dump unnamed buffers', function() + set_additional_cmd('set shada+=% hidden') + reset() + curbufmeths.set_line(0, 'foo') + nvim_command('enew') + curbufmeths.set_line(0, 'bar') + eq(2, funcs.bufnr('$')) + nvim_command('qall!') + reset() + eq(1, funcs.bufnr('$')) + eq('', funcs.bufname(1)) + end) end) diff --git a/test/functional/shada/compatibility_spec.lua b/test/functional/shada/compatibility_spec.lua index 342dee377b..1fa88c58e5 100644 --- a/test/functional/shada/compatibility_spec.lua +++ b/test/functional/shada/compatibility_spec.lua @@ -4,9 +4,8 @@ local nvim_command, funcs, eq = helpers.command, helpers.funcs, helpers.eq local exc_exec = helpers.exc_exec local shada_helpers = require('test.functional.shada.helpers') -local reset, set_additional_cmd, clear, get_shada_rw = - shada_helpers.reset, shada_helpers.set_additional_cmd, - shada_helpers.clear, shada_helpers.get_shada_rw +local reset, clear, get_shada_rw = shada_helpers.reset, shada_helpers.clear, + shada_helpers.get_shada_rw local read_shada_file = shada_helpers.read_shada_file local wshada, sdrcmd, shada_fname = get_shada_rw('Xtest-functional-shada-compatibility.shada') @@ -181,7 +180,7 @@ describe('ShaDa forward compatibility support code', function() end eq(3, found) nvim_command('wshada! ' .. shada_fname) - local found = 0 + found = 0 for i, subv in ipairs(read_shada_file(shada_fname)) do if i == 1 then eq(1, subv.type) @@ -249,7 +248,7 @@ describe('ShaDa forward compatibility support code', function() end eq(1, found) nvim_command('wshada! ' .. shada_fname) - local found = 0 + found = 0 for i, v in ipairs(read_shada_file(shada_fname)) do if i == 1 then eq(1, v.type) @@ -271,9 +270,7 @@ describe('ShaDa forward compatibility support code', function() it('works with register item with type 10', function() wshada('\005\001\019\132\161na\162rX\194\162rc\145\196\001-\162rt\010') eq(0, exc_exec(sdrcmd(true))) - -- getreg may return empty list as list with NULL pointer which API - -- translates into nil for some reason. - eq({}, funcs.getreg('a', 1, 1) or {}) + eq({}, funcs.getreg('a', 1, 1)) eq('', funcs.getregtype('a')) nvim_command('wshada ' .. shada_fname) local found = 0 @@ -289,7 +286,7 @@ describe('ShaDa forward compatibility support code', function() end eq(1, found) nvim_command('wshada! ' .. shada_fname) - local found = 0 + found = 0 for i, v in ipairs(read_shada_file(shada_fname)) do if i == 1 then eq(1, v.type) @@ -395,7 +392,7 @@ describe('ShaDa forward compatibility support code', function() end eq(1, found) nvim_command('wshada! ' .. shada_fname) - local found = 0 + found = 0 for i, v in ipairs(read_shada_file(shada_fname)) do if i == 1 then eq(1, v.type) @@ -432,7 +429,7 @@ describe('ShaDa forward compatibility support code', function() end eq(1, found) nvim_command('wshada! ' .. shada_fname) - local found = 0 + found = 0 for i, v in ipairs(read_shada_file(shada_fname)) do if i == 1 then eq(1, v.type) diff --git a/test/functional/shada/errors_spec.lua b/test/functional/shada/errors_spec.lua index 16ae77af02..e7951ee74c 100644 --- a/test/functional/shada/errors_spec.lua +++ b/test/functional/shada/errors_spec.lua @@ -1,6 +1,7 @@ -- ShaDa errors handling support local helpers = require('test.functional.helpers') -local nvim_command, eq, exc_exec = helpers.command, helpers.eq, helpers.exc_exec +local nvim_command, eq, exc_exec, redir_exec = + helpers.command, helpers.eq, helpers.exc_exec, helpers.redir_exec local shada_helpers = require('test.functional.shada.helpers') local reset, clear, get_shada_rw = @@ -124,6 +125,11 @@ describe('ShaDa error handling', function() eq('Vim(rshada):E575: Error while reading ShaDa file: search pattern entry at position 0 has sc key value which is not a boolean', exc_exec(sdrcmd())) end) + it('fails on search pattern item with NIL search_backward key value', function() + wshada('\002\000\009\130\162sX\192\162sb\192') + eq('Vim(rshada):E575: Error while reading ShaDa file: search pattern entry at position 0 has sb key value which is not a boolean', exc_exec(sdrcmd())) + end) + it('fails on search pattern item with NIL has_line_offset key value', function() wshada('\002\000\009\130\162sX\192\162sl\192') eq('Vim(rshada):E575: Error while reading ShaDa file: search pattern entry at position 0 has sl key value which is not a boolean', exc_exec(sdrcmd())) @@ -487,4 +493,21 @@ $ eq('Vim(wshada):E576: Error while reading ShaDa file: last entry specified that it occupies 47 bytes, but file ended earlier', exc_exec('wshada ' .. shada_fname)) eq(0, exc_exec('wshada! ' .. shada_fname)) end) + + it('errors when a funcref is stored in a variable', function() + nvim_command('let F = function("tr")') + nvim_command('set shada+=!') + eq('\nE951: Error while dumping variable g:F, itself: attempt to dump function reference' + .. '\nE574: Failed to write variable F', + redir_exec('wshada')) + end) + + it('errors when a self-referencing list is stored in a variable', function() + nvim_command('let L = []') + nvim_command('call add(L, L)') + nvim_command('set shada+=!') + eq('\nE952: Unable to dump variable g:L: container references itself in index 0' + .. '\nE574: Failed to write variable L', + redir_exec('wshada')) + end) end) diff --git a/test/functional/shada/helpers.lua b/test/functional/shada/helpers.lua index c2ff4cadd1..d4eb7f57bd 100644 --- a/test/functional/shada/helpers.lua +++ b/test/functional/shada/helpers.lua @@ -3,32 +3,25 @@ local spawn, set_session, meths, nvim_prog = helpers.spawn, helpers.set_session, helpers.meths, helpers.nvim_prog local write_file, merge_args = helpers.write_file, helpers.merge_args -local msgpack = require('MessagePack') +local mpack = require('mpack') local tmpname = os.tmpname() local additional_cmd = '' local function nvim_argv() - local ret - local nvim_argv = {nvim_prog, '-u', 'NONE', '-i', tmpname, '-N', - '--cmd', 'set shortmess+=I background=light noswapfile', - '--cmd', additional_cmd, - '--embed'} + local argv = {nvim_prog, '-u', 'NONE', '-i', tmpname, '-N', + '--cmd', 'set shortmess+=I background=light noswapfile', + '--cmd', additional_cmd, + '--embed'} if helpers.prepend_argv then - return merge_args(helpers.prepend_argv, nvim_argv) + return merge_args(helpers.prepend_argv, argv) else - return nvim_argv + return argv end end -local session = nil - local reset = function() - if session then - session:exit(0) - end - session = spawn(nvim_argv()) - set_session(session) + set_session(spawn(nvim_argv())) meths.set_var('tmpname', tmpname) end @@ -67,13 +60,13 @@ local read_shada_file = function(fname) local fd = io.open(fname, 'r') local mstring = fd:read('*a') fd:close() - local unpacker = msgpack.unpacker(mstring) + local unpack = mpack.Unpacker() local ret = {} - local cur + local cur, val local i = 0 - while true do - local off, val = unpacker() - if not off then break end + local off = 1 + while off <= #mstring do + val, off = unpack(mstring, off) if i % 4 == 0 then cur = {} ret[#ret + 1] = cur @@ -88,7 +81,6 @@ return { reset=reset, set_additional_cmd=set_additional_cmd, clear=clear, - exc_exec=exc_exec, get_shada_rw=get_shada_rw, read_shada_file=read_shada_file, } diff --git a/test/functional/shada/history_spec.lua b/test/functional/shada/history_spec.lua index 1123f829d2..94513945d0 100644 --- a/test/functional/shada/history_spec.lua +++ b/test/functional/shada/history_spec.lua @@ -107,14 +107,32 @@ describe('ShaDa support code', function() end) it('dumps and loads last search pattern with offset', function() - funcs.setline('.', {'foo', 'bar'}) + meths.set_option('wrapscan', false) + funcs.setline('.', {'foo', 'bar--'}) nvim_feed('gg0/a/e+1\n') eq({0, 2, 3, 0}, funcs.getpos('.')) nvim_command('wshada') reset() - funcs.setline('.', {'foo', 'bar'}) + meths.set_option('wrapscan', false) + funcs.setline('.', {'foo', 'bar--'}) nvim_feed('gg0n') eq({0, 2, 3, 0}, funcs.getpos('.')) + eq(1, meths.get_vvar('searchforward')) + end) + + it('dumps and loads last search pattern with offset and backward direction', + function() + meths.set_option('wrapscan', false) + funcs.setline('.', {'foo', 'bar--'}) + nvim_feed('G$?a?e+1\n') + eq({0, 2, 3, 0}, funcs.getpos('.')) + nvim_command('wshada') + reset() + meths.set_option('wrapscan', false) + funcs.setline('.', {'foo', 'bar--'}) + nvim_feed('G$n') + eq({0, 2, 3, 0}, funcs.getpos('.')) + eq(0, meths.get_vvar('searchforward')) end) it('saves v:hlsearch=1', function() diff --git a/test/functional/shada/marks_spec.lua b/test/functional/shada/marks_spec.lua index f03b330fb5..955a6f382b 100644 --- a/test/functional/shada/marks_spec.lua +++ b/test/functional/shada/marks_spec.lua @@ -15,15 +15,15 @@ local nvim_current_line = function() end describe('ShaDa support code', function() - testfilename = 'Xtestfile-functional-shada-marks' - testfilename_2 = 'Xtestfile-functional-shada-marks-2' + local testfilename = 'Xtestfile-functional-shada-marks' + local testfilename_2 = 'Xtestfile-functional-shada-marks-2' before_each(function() reset() local fd = io.open(testfilename, 'w') fd:write('test\n') fd:write('test2\n') fd:close() - local fd = io.open(testfilename_2, 'w') + fd = io.open(testfilename_2, 'w') fd:write('test3\n') fd:write('test4\n') fd:close() @@ -108,13 +108,15 @@ 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)) eq(tf_full, oldfiles[1]) eq(tf_full_2, oldfiles[2]) nvim_command('rshada!') - local oldfiles = meths.get_vvar('oldfiles') + 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/shada/merging_spec.lua b/test/functional/shada/merging_spec.lua index 7066ca9f54..221f989409 100644 --- a/test/functional/shada/merging_spec.lua +++ b/test/functional/shada/merging_spec.lua @@ -1,7 +1,7 @@ -- ShaDa merging data support local helpers = require('test.functional.helpers') -local nvim_command, meths, funcs, curbufmeths, eq = - helpers.command, helpers.meths, helpers.funcs, +local nvim_command, funcs, curbufmeths, eq = + helpers.command, helpers.funcs, helpers.curbufmeths, helpers.eq local exc_exec, redir_exec = helpers.exc_exec, helpers.redir_exec @@ -870,7 +870,7 @@ describe('ShaDa jumps support code', function() end wshada(shada) eq(0, exc_exec(sdrcmd())) - local shada = '' + shada = '' for i = 1,101 do local t = i * 2 shada = shada .. ( @@ -964,7 +964,7 @@ describe('ShaDa changes support code', function() end wshada(shada) eq(0, exc_exec(sdrcmd())) - local shada = '' + shada = '' for i = 1,101 do local t = i * 2 shada = shada .. ( @@ -1001,7 +1001,7 @@ describe('ShaDa changes support code', function() end wshada(shada) eq(0, exc_exec(sdrcmd())) - local shada = '' + shada = '' for i = 1,100 do shada = shada .. ('\011%c\018\131\162mX\195\161f\196\006/a/b/c\161l%c' ):format(i, i) diff --git a/test/functional/shada/registers_spec.lua b/test/functional/shada/registers_spec.lua index f0133b1086..4043d94a69 100644 --- a/test/functional/shada/registers_spec.lua +++ b/test/functional/shada/registers_spec.lua @@ -43,9 +43,9 @@ describe('ShaDa support code', function() setreg('b', {'bca', 'abc', 'cba'}, 'b3') nvim_command('qall') reset() - eq({nil, ''}, getreg('c')) - eq({nil, ''}, getreg('l')) - eq({nil, ''}, getreg('b')) + eq({{}, ''}, getreg('c')) + eq({{}, ''}, getreg('l')) + eq({{}, ''}, getreg('b')) end) it('does restore registers with zero <', function() @@ -67,9 +67,9 @@ describe('ShaDa support code', function() setreg('b', {'bca', 'abc', 'cba'}, 'b3') nvim_command('qall') reset() - eq({nil, ''}, getreg('c')) - eq({nil, ''}, getreg('l')) - eq({nil, ''}, getreg('b')) + eq({{}, ''}, getreg('c')) + eq({{}, ''}, getreg('l')) + eq({{}, ''}, getreg('b')) end) it('does restore registers with zero "', function() @@ -103,7 +103,7 @@ describe('ShaDa support code', function() nvim_command('qall') reset() eq({{'d'}, 'v'}, getreg('o')) - eq({nil, ''}, getreg('t')) + eq({{}, ''}, getreg('t')) end) it('does limit number of lines according to "', function() @@ -113,7 +113,7 @@ describe('ShaDa support code', function() nvim_command('qall') reset() eq({{'d'}, 'v'}, getreg('o')) - eq({nil, ''}, getreg('t')) + eq({{}, ''}, getreg('t')) end) it('does limit number of lines according to < rather then "', function() @@ -125,7 +125,7 @@ describe('ShaDa support code', function() reset() eq({{'d'}, 'v'}, getreg('o')) eq({{'a', 'b', 'cde'}, 'V'}, getreg('t')) - eq({nil, ''}, getreg('h')) + eq({{}, ''}, getreg('h')) end) it('dumps and loads register correctly when &encoding is not UTF-8', diff --git a/test/functional/shada/shada_spec.lua b/test/functional/shada/shada_spec.lua index 2bc855a239..683d520627 100644 --- a/test/functional/shada/shada_spec.lua +++ b/test/functional/shada/shada_spec.lua @@ -8,7 +8,7 @@ local write_file, spawn, set_session, nvim_prog, exc_exec = local lfs = require('lfs') local paths = require('test.config.paths') -local msgpack = require('MessagePack') +local mpack = require('mpack') local shada_helpers = require('test.functional.shada.helpers') local reset, clear, get_shada_rw = @@ -107,7 +107,7 @@ describe('ShaDa support code', function() end) it('reads correctly various timestamps', function() - local mpack = { + local msgpack = { '\100', -- Positive fixnum 100 '\204\255', -- uint 8 255 '\205\010\003', -- uint 16 2563 @@ -116,23 +116,23 @@ describe('ShaDa support code', function() } local s = '\100' local e = '\001\192' - wshada(s .. table.concat(mpack, e .. s) .. e) + wshada(s .. table.concat(msgpack, e .. s) .. e) eq(0, exc_exec('wshada ' .. shada_fname)) local found = 0 - local typ = select(2, msgpack.unpacker(s)()) + local typ = mpack.unpack(s) for _, v in ipairs(read_shada_file(shada_fname)) do if v.type == typ then found = found + 1 - eq(select(2, msgpack.unpacker(mpack[found])()), v.timestamp) + eq(mpack.unpack(msgpack[found]), v.timestamp) end end - eq(#mpack, found) + eq(#msgpack, found) end) it('does not write NONE file', function() local session = spawn({nvim_prog, '-u', 'NONE', '-i', 'NONE', '--embed', '--cmd', 'qall'}, true) - session:exit(0) + session:close() eq(nil, lfs.attributes('NONE')) eq(nil, lfs.attributes('NONE.tmp.a')) end) @@ -143,7 +143,7 @@ describe('ShaDa support code', function() true) set_session(session) eq('', funcs.getreg('a')) - session:exit(0) + session:close() os.remove('NONE') end) @@ -174,6 +174,7 @@ describe('ShaDa support code', function() nvim_command('set shada+=%') nvim_command('wshada! ' .. shada_fname) local readme_fname = paths.test_source_path .. '/README.md' + readme_fname = helpers.eval( 'resolve("' .. readme_fname .. '")' ) eq({[7]=1, [8]=2, [9]=1, [10]=4, [11]=1}, find_file(readme_fname)) nvim_command('set shada+=r~') nvim_command('wshada! ' .. shada_fname) diff --git a/test/functional/shada/variables_spec.lua b/test/functional/shada/variables_spec.lua index 6225971e5f..7ceeafdc71 100644 --- a/test/functional/shada/variables_spec.lua +++ b/test/functional/shada/variables_spec.lua @@ -1,7 +1,7 @@ -- ShaDa variables saving/reading support local helpers = require('test.functional.helpers') -local meths, funcs, nvim_command, eq = - helpers.meths, helpers.funcs, helpers.command, helpers.eq +local meths, funcs, nvim_command, eq, exc_exec = + helpers.meths, helpers.funcs, helpers.command, helpers.eq, helpers.exc_exec local shada_helpers = require('test.functional.shada.helpers') local reset, set_additional_cmd, clear = @@ -22,12 +22,17 @@ describe('ShaDa support code', function() eq('foo', meths.get_var('STRVAR')) end) - local autotest = function(tname, varname, varval) + local autotest = function(tname, varname, varval, val_is_expr) it('is able to dump and read back ' .. tname .. ' variable automatically', function() set_additional_cmd('set shada+=!') reset() - meths.set_var(varname, varval) + if val_is_expr then + nvim_command('let g:' .. varname .. ' = ' .. varval) + varval = meths.get_var(varname) + else + meths.set_var(varname, varval) + end -- Exit during `reset` is not a regular exit: it does not write shada -- automatically nvim_command('qall') @@ -41,6 +46,10 @@ describe('ShaDa support code', function() autotest('float', 'FLTVAR', 42.5) autotest('dictionary', 'DCTVAR', {a=10}) autotest('list', 'LSTVAR', {{a=10}, {b=10.5}, {c='str'}}) + autotest('true', 'TRUEVAR', true) + autotest('false', 'FALSEVAR', false) + autotest('null', 'NULLVAR', 'v:null', true) + autotest('ext', 'EXTVAR', '{"_TYPE": v:msgpack_types.ext, "_VAL": [2, ["", ""]]}', true) it('does not read back variables without `!` in &shada', function() meths.set_var('STRVAR', 'foo') @@ -136,4 +145,31 @@ describe('ShaDa support code', function() eq({['\171']={{'\171'}, {['\171']='\171'}, {a='Test'}}}, meths.get_var('NESTEDVAR')) end) + + it('errors and writes when a funcref is stored in a variable', + function() + nvim_command('let F = function("tr")') + meths.set_var('U', '10') + nvim_command('set shada+=!') + set_additional_cmd('set shada+=!') + eq('Vim(wshada):E951: Error while dumping variable g:F, itself: attempt to dump function reference', + exc_exec('wshada')) + meths.set_option('shada', '') + reset() + eq('10', meths.get_var('U')) + end) + + it('errors and writes when a self-referencing list is stored in a variable', + function() + meths.set_var('L', {}) + nvim_command('call add(L, L)') + meths.set_var('U', '10') + nvim_command('set shada+=!') + eq('Vim(wshada):E952: Unable to dump variable g:L: container references itself in index 0', + exc_exec('wshada')) + meths.set_option('shada', '') + set_additional_cmd('set shada+=!') + reset() + eq('10', meths.get_var('U')) + end) end) diff --git a/test/functional/shell/viml_system_spec.lua b/test/functional/shell/viml_system_spec.lua index 4985c24aec..00b16e9158 100644 --- a/test/functional/shell/viml_system_spec.lua +++ b/test/functional/shell/viml_system_spec.lua @@ -133,7 +133,7 @@ describe('system()', function() -- write more than 1mb of data, which should be enough to overcome -- the os buffer limit and force multiple event loop iterations to write -- everything - for i = 1, 0xffff do + for _ = 1, 0xffff do input[#input + 1] = '01234567890ABCDEFabcdef' end input = table.concat(input, '\n') @@ -299,7 +299,7 @@ describe('systemlist()', function() describe('passing a lot of input', function() it('returns the program output', function() local input = {} - for i = 1, 0xffff do + for _ = 1, 0xffff do input[#input + 1] = '01234567890ABCDEFabcdef' end nvim('set_var', 'input', input) diff --git a/test/functional/terminal/altscreen_spec.lua b/test/functional/terminal/altscreen_spec.lua index 9ec0fc7c5a..d9d96b25f9 100644 --- a/test/functional/terminal/altscreen_spec.lua +++ b/test/functional/terminal/altscreen_spec.lua @@ -1,6 +1,5 @@ local helpers = require('test.functional.helpers') local thelpers = require('test.functional.terminal.helpers') -local Screen = require('test.functional.ui.screen') local clear, eq, curbuf = helpers.clear, helpers.eq, helpers.curbuf local feed = helpers.feed local feed_data = thelpers.feed_data diff --git a/test/functional/terminal/buffer_spec.lua b/test/functional/terminal/buffer_spec.lua index ffdfec4428..cefb603a7e 100644 --- a/test/functional/terminal/buffer_spec.lua +++ b/test/functional/terminal/buffer_spec.lua @@ -1,5 +1,4 @@ local helpers = require('test.functional.helpers') -local Screen = require('test.functional.ui.screen') local thelpers = require('test.functional.terminal.helpers') local feed, clear, nvim = helpers.feed, helpers.clear, helpers.nvim local wait = helpers.wait @@ -159,8 +158,7 @@ describe('terminal buffer', function() end) it('handles loss of focus gracefully', function() - -- Temporarily change the statusline to avoid printing the file name, which - -- varies be where the test is run. + -- Change the statusline to avoid printing the file name, which varies. nvim('set_option', 'statusline', '==========') execute('set laststatus=0') @@ -196,5 +194,15 @@ describe('terminal buffer', function() execute('set laststatus=1') -- Restore laststatus to the default. end) + + it('term_close() use-after-free #4393', function() + if eval("executable('yes')") == 0 then + pending('missing "yes" command') + return + end + execute('terminal yes') + feed([[<C-\><C-n>]]) + execute('bdelete!') + end) end) diff --git a/test/functional/terminal/cursor_spec.lua b/test/functional/terminal/cursor_spec.lua index 7f07467fde..c15da2f760 100644 --- a/test/functional/terminal/cursor_spec.lua +++ b/test/functional/terminal/cursor_spec.lua @@ -2,7 +2,7 @@ local helpers = require('test.functional.helpers') local Screen = require('test.functional.ui.screen') local thelpers = require('test.functional.terminal.helpers') local feed, clear, nvim = helpers.feed, helpers.clear, helpers.nvim -local nvim_dir, execute, eq = helpers.nvim_dir, helpers.execute, helpers.eq +local nvim_dir, execute = helpers.nvim_dir, helpers.execute local hide_cursor = thelpers.hide_cursor local show_cursor = thelpers.show_cursor @@ -59,7 +59,7 @@ describe('terminal cursor', function() ]]) end) - it('is positioned correctly when focused', function() + pending('is positioned correctly when focused', function() feed('i') screen:expect([[ 1 tty ready | diff --git a/test/functional/terminal/edit_spec.lua b/test/functional/terminal/edit_spec.lua new file mode 100644 index 0000000000..dcc4a54610 --- /dev/null +++ b/test/functional/terminal/edit_spec.lua @@ -0,0 +1,75 @@ +local helpers = require('test.functional.helpers') +local screen = require('test.functional.ui.screen') + +local curbufmeths = helpers.curbufmeths +local curwinmeths = helpers.curwinmeths +local nvim_dir = helpers.nvim_dir +local command = helpers.command +local meths = helpers.meths +local clear = helpers.clear +local eq = helpers.eq + +describe(':edit term://*', function() + local get_screen = function(columns, lines) + local scr = screen.new(columns, lines) + scr:attach(false) + return scr + end + + before_each(function() + clear() + meths.set_option('shell', nvim_dir .. '/shell-test') + meths.set_option('shellcmdflag', 'EXE') + end) + + it('runs TermOpen event', function() + meths.set_var('termopen_runs', {}) + command('autocmd TermOpen * :call add(g:termopen_runs, expand("<amatch>"))') + command('edit term://') + local termopen_runs = meths.get_var('termopen_runs') + eq(1, #termopen_runs) + eq(termopen_runs[1], termopen_runs[1]:match('^term://.//%d+:$')) + end) + + it('runs TermOpen early enough to respect terminal_scrollback_buffer_size', function() + local columns, lines = 20, 4 + local scr = get_screen(columns, lines) + local rep = 'a' + meths.set_option('shellcmdflag', 'REP ' .. rep) + local rep_size = rep:byte() + local sb = 10 + local gsb = 20 + meths.set_var('terminal_scrollback_buffer_size', gsb) + command('autocmd TermOpen * :let b:terminal_scrollback_buffer_size = ' + .. tostring(sb)) + command('edit term://foobar') + local bufcontents = {} + local winheight = curwinmeths.get_height() + -- I have no idea why there is + 4 needed. But otherwise it works fine with + -- different scrollbacks. + local shift = -4 + local buf_cont_start = rep_size - 1 - sb - winheight - shift + local bufline = function(i) return ('%d: foobar'):format(i) end + for i = buf_cont_start,(rep_size - 1) do + bufcontents[#bufcontents + 1] = bufline(i) + end + bufcontents[#bufcontents + 1] = '' + bufcontents[#bufcontents + 1] = '[Process exited 0]' + -- Do not ask me why displayed screen is one line *before* buffer + -- contents: buffer starts with 87:, screen with 86:. + local exp_screen = '\n' + local did_cursor = false + for i = 0,(winheight - 1) do + local line = bufline(buf_cont_start + i - 1) + exp_screen = (exp_screen + .. (did_cursor and '' or '^') + .. line + .. (' '):rep(columns - #line) + .. '|\n') + did_cursor = true + end + exp_screen = exp_screen .. (' '):rep(columns) .. '|\n' + scr:expect(exp_screen) + eq(bufcontents, curbufmeths.get_lines(1, -1, true)) + end) +end) diff --git a/test/functional/terminal/ex_terminal_spec.lua b/test/functional/terminal/ex_terminal_spec.lua index 3855cf4b65..d89092ff27 100644 --- a/test/functional/terminal/ex_terminal_spec.lua +++ b/test/functional/terminal/ex_terminal_spec.lua @@ -1,16 +1,15 @@ local helpers = require('test.functional.helpers') local Screen = require('test.functional.ui.screen') local clear, wait, nvim = helpers.clear, helpers.wait, helpers.nvim -local nvim_dir = helpers.nvim_dir -local execute, source = helpers.execute, helpers.source -local eq, neq = helpers.eq, helpers.neq +local nvim_dir, source, eq = helpers.nvim_dir, helpers.source, helpers.eq +local execute, eval = helpers.execute, helpers.eval describe(':terminal', function() local screen before_each(function() clear() - screen = Screen.new(50, 7) + screen = Screen.new(50, 4) screen:attach(false) nvim('set_option', 'shell', nvim_dir..'/shell-test') nvim('set_option', 'shellcmdflag', 'EXE') @@ -22,10 +21,7 @@ describe(':terminal', function() wait() screen:expect([[ ready $ | - [Program exited, press any key to close] | - | - | - | + [Process exited 0] | | -- TERMINAL -- | ]]) @@ -37,10 +33,7 @@ describe(':terminal', function() screen:expect([[ ready $ echo hi | | - [Program exited, press any key to close] | - | - | - | + [Process exited 0] | -- TERMINAL -- | ]]) end) @@ -51,11 +44,16 @@ describe(':terminal', function() screen:expect([[ ready $ echo 'hello' \ "world" | | - [Program exited, press any key to close] | - | - | - | + [Process exited 0] | -- TERMINAL -- | ]]) end) + + it('ex_terminal() double-free #4554', function() + source([[ + autocmd BufNew * set shell=foo + terminal]]) + -- Verify that BufNew actually fired (else the test is invalid). + eq('foo', eval('&shell')) + end) end) diff --git a/test/functional/terminal/helpers.lua b/test/functional/terminal/helpers.lua index ae13aab277..a32ae650d6 100644 --- a/test/functional/terminal/helpers.lua +++ b/test/functional/terminal/helpers.lua @@ -72,7 +72,7 @@ local function screen_setup(extra_height, command) empty_line, empty_line, } - for i = 1, extra_height do + for _ = 1, extra_height do table.insert(expected, empty_line) end diff --git a/test/functional/terminal/highlight_spec.lua b/test/functional/terminal/highlight_spec.lua index 1a96cb4dba..045f5aa42f 100644 --- a/test/functional/terminal/highlight_spec.lua +++ b/test/functional/terminal/highlight_spec.lua @@ -40,7 +40,7 @@ describe('terminal window highlighting', function() ]]) end) - function descr(title, attr_num, set_attrs_fn) + local function descr(title, attr_num, set_attrs_fn) local function sub(s) return s:gsub('NUM', attr_num) end diff --git a/test/functional/terminal/mouse_spec.lua b/test/functional/terminal/mouse_spec.lua index ac61abebcb..c4bd3c2663 100644 --- a/test/functional/terminal/mouse_spec.lua +++ b/test/functional/terminal/mouse_spec.lua @@ -1,8 +1,7 @@ -local Screen = require('test.functional.ui.screen') local helpers = require('test.functional.helpers') local thelpers = require('test.functional.terminal.helpers') -local clear, eq, curbuf = helpers.clear, helpers.eq, helpers.curbuf -local feed, execute, nvim = helpers.feed, helpers.execute, helpers.nvim +local clear = helpers.clear +local feed, nvim = helpers.feed, helpers.nvim local feed_data = thelpers.feed_data describe('terminal mouse', function() 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 d38bedcd4a..364ca327a4 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -1,10 +1,10 @@ -- Some sanity checks for the TUI using the builtin terminal emulator -- as a simple way to send keys and assert screen state. -local Screen = require('test.functional.ui.screen') local helpers = require('test.functional.helpers') local thelpers = require('test.functional.terminal.helpers') local feed = thelpers.feed_data local execute = helpers.execute +local nvim_dir = helpers.nvim_dir describe('tui', function() local screen @@ -41,7 +41,7 @@ describe('tui', function() -- INSERT -- | -- TERMINAL -- | ]]) - feed('\x1b') + feed('\027') screen:expect([[ abc | test1 | @@ -53,11 +53,11 @@ describe('tui', function() ]]) end) - it('interprets leading esc byte as the alt modifier', function() + it('interprets leading <Esc> byte as ALT modifier in normal-mode', function() local keys = 'dfghjkl' for c in keys:gmatch('.') do execute('nnoremap <a-'..c..'> ialt-'..c..'<cr><esc>') - feed('\x1b'..c) + feed('\027'..c) end screen:expect([[ alt-j | @@ -80,11 +80,30 @@ describe('tui', function() ]]) end) + it('does not mangle unmapped ALT-key chord', function() + -- Vim represents ALT/META by setting the "high bit" of the modified key; + -- we do _not_. #3982 + -- + -- Example: for input ALT+j: + -- * Vim (Nvim prior to #3982) sets high-bit, inserts "ê". + -- * Nvim (after #3982) inserts "j". + feed('i\027j') + screen:expect([[ + j{1: } | + ~ | + ~ | + ~ | + [No Name] [+] | + -- INSERT -- | + -- TERMINAL -- | + ]]) + end) + it('accepts ascii control sequences', function() feed('i') - feed('\x16\x07') -- ctrl+g - feed('\x16\x16') -- ctrl+v - feed('\x16\x0d') -- ctrl+m + feed('\022\007') -- ctrl+g + feed('\022\022') -- ctrl+v + feed('\022\013') -- ctrl+m screen:expect([[ {3:^G^V^M}{1: } | ~ | @@ -97,7 +116,7 @@ describe('tui', function() end) it('automatically sends <Paste> for bracketed paste sequences', function() - feed('i\x1b[200~') + feed('i\027[200~') screen:expect([[ {1: } | ~ | @@ -117,7 +136,7 @@ describe('tui', function() -- INSERT (paste) -- | -- TERMINAL -- | ]]) - feed('\x1b[201~') + feed('\027[201~') screen:expect([[ pasted from terminal{1: } | ~ | @@ -135,9 +154,7 @@ describe('tui', function() for i = 1, 3000 do t[i] = 'item ' .. tostring(i) end - feed('i\x1b[200~') - feed(table.concat(t, '\n')) - feed('\x1b[201~') + feed('i\027[200~'..table.concat(t, '\n')..'\027[201~') screen:expect([[ item 2997 | item 2998 | @@ -149,3 +166,138 @@ describe('tui', function() ]]) end) end) + +describe('tui with non-tty file descriptors', function() + before_each(helpers.clear) + + after_each(function() + os.remove('testF') -- ensure test file is removed + end) + + it('can handle pipes as stdout and stderr', function() + local screen = thelpers.screen_setup(0, '"'..helpers.nvim_prog..' -u NONE -i NONE --cmd \'set noswapfile\' --cmd \'normal iabc\' > /dev/null 2>&1 && cat testF && rm testF"') + screen:set_default_attr_ids({}) + screen:set_default_attr_ignore(true) + feed(':w testF\n:q\n') + screen:expect([[ + :w testF | + :q | + abc | + | + [Process exited 0] | + | + -- TERMINAL -- | + ]]) + end) +end) + +describe('tui focus event handling', function() + local screen + + before_each(function() + helpers.clear() + screen = thelpers.screen_setup(0, '["'..helpers.nvim_prog..'", "-u", "NONE", "-i", "NONE", "--cmd", "set noswapfile"]') + execute('autocmd FocusGained * echo "gained"') + execute('autocmd FocusLost * echo "lost"') + end) + + it('can handle focus events in normal mode', function() + feed('\027[I') + screen:expect([[ + {1: } | + ~ | + ~ | + ~ | + [No Name] | + gained | + -- TERMINAL -- | + ]]) + + feed('\027[O') + screen:expect([[ + {1: } | + ~ | + ~ | + ~ | + [No Name] | + lost | + -- TERMINAL -- | + ]]) + end) + + it('can handle focus events in insert mode', function() + execute('set noshowmode') + feed('i') + feed('\027[I') + screen:expect([[ + {1: } | + ~ | + ~ | + ~ | + [No Name] | + gained | + -- TERMINAL -- | + ]]) + feed('\027[O') + screen:expect([[ + {1: } | + ~ | + ~ | + ~ | + [No Name] | + lost | + -- TERMINAL -- | + ]]) + end) + + it('can handle focus events in cmdline mode', function() + feed(':') + feed('\027[I') + screen:expect([[ + | + ~ | + ~ | + ~ | + [No Name] | + g{1:a}ined | + -- TERMINAL -- | + ]]) + feed('\027[O') + screen:expect([[ + | + ~ | + ~ | + ~ | + [No Name] | + l{1:o}st | + -- TERMINAL -- | + ]]) + end) + + it('can handle focus events in terminal mode', function() + execute('set shell='..nvim_dir..'/shell-test') + execute('set laststatus=0') + execute('set noshowmode') + execute('terminal') + feed('\027[I') + screen:expect([[ + ready $ | + [Process exited 0]{1: } | + | + | + | + gained | + -- TERMINAL -- | + ]]) + feed('\027[O') + screen:expect([[ + ready $ | + [Process exited 0]{1: } | + | + | + | + lost | + -- TERMINAL -- | + ]]) + end) +end) diff --git a/test/functional/terminal/window_spec.lua b/test/functional/terminal/window_spec.lua index c2b9390a11..6c236ed868 100644 --- a/test/functional/terminal/window_spec.lua +++ b/test/functional/terminal/window_spec.lua @@ -1,7 +1,7 @@ local helpers = require('test.functional.helpers') local thelpers = require('test.functional.terminal.helpers') -local feed, clear, nvim = helpers.feed, helpers.clear, helpers.nvim -local wait, eq = helpers.wait, helpers.eq +local feed, clear = helpers.feed, helpers.clear +local wait = helpers.wait describe('terminal window', function() diff --git a/test/functional/terminal/window_split_tab_spec.lua b/test/functional/terminal/window_split_tab_spec.lua index c102b1f133..727eba2717 100644 --- a/test/functional/terminal/window_split_tab_spec.lua +++ b/test/functional/terminal/window_split_tab_spec.lua @@ -1,8 +1,7 @@ local helpers = require('test.functional.helpers') local thelpers = require('test.functional.terminal.helpers') -local clear, eq, curbuf = helpers.clear, helpers.eq, helpers.curbuf +local clear = helpers.clear local feed, nvim = helpers.feed, helpers.nvim -local feed_data = thelpers.feed_data describe('terminal', function() local screen diff --git a/test/functional/ui/bufhl_spec.lua b/test/functional/ui/bufhl_spec.lua new file mode 100644 index 0000000000..58f5b11de0 --- /dev/null +++ b/test/functional/ui/bufhl_spec.lua @@ -0,0 +1,261 @@ +local helpers = require('test.functional.helpers') +local Screen = require('test.functional.ui.screen') +local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert +local execute, request, neq = helpers.execute, helpers.request, helpers.neq + + +describe('Buffer highlighting', function() + local screen + local curbuf + + local hl_colors = { + NonText = Screen.colors.Blue, + Question = Screen.colors.SeaGreen, + String = Screen.colors.Fuchsia, + Statement = Screen.colors.Brown, + Special = Screen.colors.SlateBlue, + Identifier = Screen.colors.DarkCyan + } + + before_each(function() + clear() + execute("syntax on") + screen = Screen.new(40, 8) + screen:attach() + screen:set_default_attr_ignore( {{bold=true, foreground=hl_colors.NonText}} ) + screen:set_default_attr_ids({ + [1] = {foreground = hl_colors.String}, + [2] = {foreground = hl_colors.Statement, bold = true}, + [3] = {foreground = hl_colors.Special}, + [4] = {bold = true, foreground = hl_colors.Special}, + [5] = {foreground = hl_colors.Identifier}, + [6] = {bold = true}, + [7] = {underline = true, bold = true, foreground = hl_colors.Special}, + [8] = {foreground = hl_colors.Special, underline = true} + }) + curbuf = request('vim_get_current_buffer') + end) + + after_each(function() + screen:detach() + end) + + local function add_hl(...) + return request('buffer_add_highlight', curbuf, ...) + end + + local function clear_hl(...) + return request('buffer_clear_highlight', curbuf, ...) + end + + + it('works', function() + insert([[ + these are some lines + with colorful text]]) + feed('+') + + screen:expect([[ + these are some lines | + with colorful tex^t | + ~ | + ~ | + ~ | + ~ | + ~ | + | + ]]) + + add_hl(-1, "String", 0 , 10, 14) + add_hl(-1, "Statement", 1 , 5, -1) + + screen:expect([[ + these are {1:some} lines | + with {2:colorful tex^t} | + ~ | + ~ | + ~ | + ~ | + ~ | + | + ]]) + + feed("ggo<esc>") + screen:expect([[ + these are {1:some} lines | + ^ | + with {2:colorful text} | + ~ | + ~ | + ~ | + ~ | + | + ]]) + + clear_hl(-1, 0 , -1) + screen:expect([[ + these are some lines | + ^ | + with colorful text | + ~ | + ~ | + ~ | + ~ | + | + ]]) + end) + + describe('support adding multiple sources', function() + local id1, id2 + before_each(function() + insert([[ + a longer example + in order to demonstrate + combining highlights + from different sources]]) + + execute("hi ImportantWord gui=bold cterm=bold") + id1 = add_hl(0, "ImportantWord", 0, 2, 8) + add_hl(id1, "ImportantWord", 1, 12, -1) + add_hl(id1, "ImportantWord", 2, 0, 9) + add_hl(id1, "ImportantWord", 3, 5, 14) + + id2 = add_hl(0, "Special", 0, 2, 8) + add_hl(id2, "Identifier", 1, 3, 8) + add_hl(id2, "Special", 1, 14, 20) + add_hl(id2, "Underlined", 2, 6, 12) + add_hl(id2, "Underlined", 3, 0, 9) + neq(id1, id2) + + screen:expect([[ + a {4:longer} example | + in {5:order} to {6:de}{4:monstr}{6:ate} | + {6:combin}{7:ing}{8: hi}ghlights | + {8:from }{7:diff}{6:erent} source^s | + ~ | + ~ | + ~ | + :hi ImportantWord gui=bold cterm=bold | + ]]) + end) + + it('and clearing the first added', function() + clear_hl(id1, 0, -1) + screen:expect([[ + a {3:longer} example | + in {5:order} to de{3:monstr}ate | + combin{8:ing hi}ghlights | + {8:from diff}erent source^s | + ~ | + ~ | + ~ | + :hi ImportantWord gui=bold cterm=bold | + ]]) + end) + + it('and clearing the second added', function() + clear_hl(id2, 0, -1) + screen:expect([[ + a {6:longer} example | + in order to {6:demonstrate} | + {6:combining} highlights | + from {6:different} source^s | + ~ | + ~ | + ~ | + :hi ImportantWord gui=bold cterm=bold | + ]]) + end) + + it('and clearing line ranges', function() + clear_hl(-1, 0, 1) + clear_hl(id1, 1, 2) + clear_hl(id2, 2, -1) + screen:expect([[ + a longer example | + in {5:order} to de{3:monstr}ate | + {6:combining} highlights | + from {6:different} source^s | + ~ | + ~ | + ~ | + :hi ImportantWord gui=bold cterm=bold | + ]]) + end) + + it('and renumbering lines', function() + feed('3Gddggo<esc>') + screen:expect([[ + a {4:longer} example | + ^ | + in {5:order} to {6:de}{4:monstr}{6:ate} | + {8:from }{7:diff}{6:erent} sources | + ~ | + ~ | + ~ | + | + ]]) + + execute(':3move 4') + screen:expect([[ + a {4:longer} example | + | + {8:from }{7:diff}{6:erent} sources | + ^in {5:order} to {6:de}{4:monstr}{6:ate} | + ~ | + ~ | + ~ | + ::3move 4 | + ]]) + end) + end) + + it('prioritizes latest added highlight', function() + insert([[ + three overlapping colors]]) + add_hl(0, "Identifier", 0, 6, 17) + add_hl(0, "String", 0, 14, 23) + local id = add_hl(0, "Special", 0, 0, 9) + + screen:expect([[ + {3:three ove}{5:rlapp}{1:ing color}^s | + ~ | + ~ | + ~ | + ~ | + ~ | + ~ | + | + ]]) + + clear_hl(id, 0, 1) + screen:expect([[ + three {5:overlapp}{1:ing color}^s | + ~ | + ~ | + ~ | + ~ | + ~ | + ~ | + | + ]]) + end) + + it('works with multibyte text', function() + insert([[ + Ta båten över sjön!]]) + add_hl(-1, "Identifier", 0, 3, 9) + add_hl(-1, "String", 0, 16, 21) + + screen:expect([[ + Ta {5:båten} över {1:sjön}^! | + ~ | + ~ | + ~ | + ~ | + ~ | + ~ | + | + ]]) + end) +end) diff --git a/test/functional/ui/highlight_spec.lua b/test/functional/ui/highlight_spec.lua index 33a53ef201..06139277b2 100644 --- a/test/functional/ui/highlight_spec.lua +++ b/test/functional/ui/highlight_spec.lua @@ -1,6 +1,7 @@ local helpers = require('test.functional.helpers') local Screen = require('test.functional.ui.screen') -local clear, feed, nvim = helpers.clear, helpers.feed, helpers.nvim +local os = require('os') +local clear, feed = helpers.clear, helpers.feed local execute, request, eq = helpers.execute, helpers.request, helpers.eq @@ -14,14 +15,72 @@ describe('color scheme compatibility', function() request('vim_set_option', 't_Co', '88') eq('88', request('vim_eval', '&t_Co')) end) +end) + +describe('manual syntax highlight', function() + -- When using manual syntax highlighting, it should be preserved even when + -- switching buffers... bug did only occur without :set hidden + -- Ref: vim patch 7.4.1236 + local screen - it('emulates gui_running when a rgb UI is attached', function() - eq(0, request('vim_eval', 'has("gui_running")')) - local screen = Screen.new() + before_each(function() + clear() + screen = Screen.new(20,5) screen:attach() - eq(1, request('vim_eval', 'has("gui_running")')) + --ignore highligting of ~-lines + screen:set_default_attr_ignore( {{bold=true, foreground=Screen.colors.Blue}} ) + --syntax highlight for vimcscripts "echo" + screen:set_default_attr_ids( {[1] = {bold=true, foreground=Screen.colors.Brown}} ) + end) + + after_each(function() screen:detach() - eq(0, request('vim_eval', 'has("gui_running")')) + os.remove('Xtest-functional-ui-highlight.tmp.vim') + end) + + -- test with "set hidden" even if the bug did not occur this way + it("works with buffer switch and 'hidden'", function() + execute('e tmp1.vim') + execute('e Xtest-functional-ui-highlight.tmp.vim') + execute('filetype on') + execute('syntax manual') + execute('set ft=vim') + execute('set syntax=ON') + feed('iecho 1<esc>0') + + execute('set hidden') + execute('w') + execute('bn') + execute('bp') + screen:expect([[ + {1:^echo} 1 | + ~ | + ~ | + ~ | + <f 1 --100%-- col 1 | + ]]) + end) + + it("works with buffer switch and 'nohidden'", function() + execute('e tmp1.vim') + execute('e Xtest-functional-ui-highlight.tmp.vim') + execute('filetype on') + execute('syntax manual') + execute('set ft=vim') + execute('set syntax=ON') + feed('iecho 1<esc>0') + + execute('set nohidden') + execute('w') + execute('bn') + execute('bp') + screen:expect([[ + {1:^echo} 1 | + ~ | + ~ | + ~ | + <ht.tmp.vim" 1L, 7C | + ]]) end) end) @@ -47,6 +106,7 @@ describe('Default highlight groups', function() after_each(function() screen:detach() end) + it('window status bar', function() screen:set_default_attr_ids({ [1] = {reverse = true, bold = true}, -- StatusLine @@ -202,4 +262,44 @@ describe('Default highlight groups', function() ]], {[1] = {bold = true, foreground = hlgroup_colors.Question}}) feed('<cr>') -- skip the "Press ENTER..." state or tests will hang end) + it('can be cleared and linked to other highlight groups', function() + execute('highlight clear ModeMsg') + feed('i') + screen:expect([[ + ^ | + ~ | + ~ | + ~ | + ~ | + ~ | + ~ | + ~ | + ~ | + ~ | + ~ | + ~ | + ~ | + -- INSERT -- | + ]], {}) + feed('<esc>') + execute('highlight CustomHLGroup guifg=red guibg=green') + execute('highlight link ModeMsg CustomHLGroup') + feed('i') + screen:expect([[ + ^ | + ~ | + ~ | + ~ | + ~ | + ~ | + ~ | + ~ | + ~ | + ~ | + ~ | + ~ | + ~ | + {1:-- INSERT --} | + ]], {[1] = {foreground = Screen.colors.Red, background = Screen.colors.Green}}) + end) end) diff --git a/test/functional/ui/input_spec.lua b/test/functional/ui/input_spec.lua index a7c8e02def..6f5cadaf81 100644 --- a/test/functional/ui/input_spec.lua +++ b/test/functional/ui/input_spec.lua @@ -8,7 +8,6 @@ describe('mappings', function() local cid local add_mapping = function(mapping, send) - local str = 'mapped '..mapping local cmd = "nnoremap "..mapping.." :call rpcnotify("..cid..", 'mapped', '" ..send:gsub('<', '<lt>').."')<cr>" execute(cmd) @@ -26,6 +25,9 @@ describe('mappings', function() add_mapping('<s-up>', '<s-up>') add_mapping('<c-s-up>', '<c-s-up>') add_mapping('<c-s-a-up>', '<c-s-a-up>') + add_mapping('<c-s-a-d-up>', '<c-s-a-d-up>') + add_mapping('<c-d-a>', '<c-d-a>') + add_mapping('<d-1>', '<d-1>') end) it('ok', function() @@ -38,6 +40,12 @@ describe('mappings', function() check_mapping('<s-a-c-up>', '<c-s-a-up>') check_mapping('<a-c-s-up>', '<c-s-a-up>') check_mapping('<a-s-c-up>', '<c-s-a-up>') + check_mapping('<c-s-a-d-up>', '<c-s-a-d-up>') + check_mapping('<s-a-d-c-up>', '<c-s-a-d-up>') + check_mapping('<d-s-a-c-up>', '<c-s-a-d-up>') + check_mapping('<c-d-a>', '<c-d-a>') + check_mapping('<d-c-a>', '<c-d-a>') + check_mapping('<d-1>', '<d-1>') end) end) diff --git a/test/functional/ui/mouse_spec.lua b/test/functional/ui/mouse_spec.lua index 30f37a7463..d0d791308b 100644 --- a/test/functional/ui/mouse_spec.lua +++ b/test/functional/ui/mouse_spec.lua @@ -1,7 +1,8 @@ local helpers = require('test.functional.helpers') local Screen = require('test.functional.ui.screen') -local clear, feed, nvim = helpers.clear, helpers.feed, helpers.nvim +local clear, feed, meths = helpers.clear, helpers.feed, helpers.meths local insert, execute = helpers.insert, helpers.execute +local eq, funcs = helpers.eq, helpers.funcs describe('Mouse input', function() local screen @@ -13,10 +14,11 @@ describe('Mouse input', function() before_each(function() clear() - nvim('set_option', 'mouse', 'a') + meths.set_option('mouse', 'a') + meths.set_option('listchars', 'eol:$') -- set mouset to very high value to ensure that even in valgrind/travis, -- nvim will still pick multiple clicks - nvim('set_option', 'mouset', 5000) + meths.set_option('mouset', 5000) screen = Screen.new(25, 5) screen:attach() screen:set_default_attr_ids({ @@ -57,31 +59,149 @@ describe('Mouse input', function() ]]) end) - it('left click in tabline switches to tab', function() + describe('tabline', function() local tab_attrs = { tab = { background=Screen.colors.LightGrey, underline=true }, sel = { bold=true }, fill = { reverse=true } } - execute('%delete') - insert('this is foo') - execute('silent file foo | tabnew | file bar') - insert('this is bar') - screen:expect([[ - {tab: + foo }{sel: + bar }{fill: }{tab:X}| - this is ba^r | - ~ | - ~ | - | - ]], tab_attrs) - feed('<LeftMouse><4,0>') - screen:expect([[ - {sel: + foo }{tab: + bar }{fill: }{tab:X}| - this is fo^o | - ~ | - ~ | - | - ]], tab_attrs) + + it('left click in default tabline (position 4) switches to tab', function() + execute('%delete') + insert('this is foo') + execute('silent file foo | tabnew | file bar') + insert('this is bar') + screen:expect([[ + {tab: + foo }{sel: + bar }{fill: }{tab:X}| + this is ba^r | + ~ | + ~ | + | + ]], tab_attrs) + feed('<LeftMouse><4,0>') + screen:expect([[ + {sel: + foo }{tab: + bar }{fill: }{tab:X}| + this is fo^o | + ~ | + ~ | + | + ]], tab_attrs) + end) + + it('left click in default tabline (position 24) closes tab', function() + meths.set_option('hidden', true) + execute('%delete') + insert('this is foo') + execute('silent file foo | tabnew | file bar') + insert('this is bar') + screen:expect([[ + {tab: + foo }{sel: + bar }{fill: }{tab:X}| + this is ba^r | + ~ | + ~ | + | + ]], tab_attrs) + feed('<LeftMouse><24,0>') + screen:expect([[ + this is fo^o | + ~ | + ~ | + ~ | + | + ]], tab_attrs) + end) + + it('double click in default tabline (position 4) opens new tab', function() + meths.set_option('hidden', true) + execute('%delete') + insert('this is foo') + execute('silent file foo | tabnew | file bar') + insert('this is bar') + screen:expect([[ + {tab: + foo }{sel: + bar }{fill: }{tab:X}| + this is ba^r | + ~ | + ~ | + | + ]], tab_attrs) + feed('<2-LeftMouse><4,0>') + screen:expect([[ + {sel: Name] }{tab: + foo + bar }{fill: }{tab:X}| + ^ | + ~ | + ~ | + | + ]], tab_attrs) + end) + + describe('%@ label', function() + before_each(function() + execute([[ + function Test(...) + let g:reply = a:000 + return copy(a:000) " Check for memory leaks: return should be freed + endfunction + ]]) + execute([[ + function Test2(...) + return call('Test', a:000 + [2]) + endfunction + ]]) + meths.set_option('tabline', '%@Test@test%X-%5@Test2@test2') + meths.set_option('showtabline', 2) + screen:expect([[ + {fill:test-test2 }| + mouse | + support and selectio^n | + ~ | + | + ]], tab_attrs) + meths.set_var('reply', {}) + end) + + local check_reply = function(expected) + eq(expected, meths.get_var('reply')) + meths.set_var('reply', {}) + end + + local test_click = function(name, click_str, click_num, mouse_button, + modifiers) + it(name .. ' works', function() + eq(1, funcs.has('tablineat')) + feed(click_str .. '<3,0>') + check_reply({0, click_num, mouse_button, modifiers}) + feed(click_str .. '<4,0>') + check_reply({}) + feed(click_str .. '<6,0>') + check_reply({5, click_num, mouse_button, modifiers, 2}) + feed(click_str .. '<13,0>') + check_reply({5, click_num, mouse_button, modifiers, 2}) + end) + end + + test_click('single left click', '<LeftMouse>', 1, 'l', ' ') + test_click('shifted single left click', '<S-LeftMouse>', 1, 'l', 's ') + test_click('shifted single left click with alt modifier', + '<S-A-LeftMouse>', 1, 'l', 's a ') + test_click('shifted single left click with alt and ctrl modifiers', + '<S-C-A-LeftMouse>', 1, 'l', 'sca ') + -- <C-RightMouse> does not work + test_click('shifted single right click with alt modifier', + '<S-A-RightMouse>', 1, 'r', 's a ') + -- Modifiers do not work with MiddleMouse + test_click('shifted single middle click with alt and ctrl modifiers', + '<MiddleMouse>', 1, 'm', ' ') + -- Modifiers do not work with N-*Mouse + test_click('double left click', '<2-LeftMouse>', 2, 'l', ' ') + test_click('triple left click', '<3-LeftMouse>', 3, 'l', ' ') + test_click('quadruple left click', '<4-LeftMouse>', 4, 'l', ' ') + test_click('double right click', '<2-RightMouse>', 2, 'r', ' ') + test_click('triple right click', '<3-RightMouse>', 3, 'r', ' ') + test_click('quadruple right click', '<4-RightMouse>', 4, 'r', ' ') + test_click('double middle click', '<2-MiddleMouse>', 2, 'm', ' ') + test_click('triple middle click', '<3-MiddleMouse>', 3, 'm', ' ') + test_click('quadruple middle click', '<4-MiddleMouse>', 4, 'm', ' ') + end) end) it('left drag changes visual selection', function() @@ -210,7 +330,7 @@ describe('Mouse input', function() end) it('ctrl + left click will search for a tag', function() - nvim('set_option', 'tags', './non-existent-tags-file') + meths.set_option('tags', './non-existent-tags-file') feed('<C-LeftMouse><0,0>') screen:expect([[ E433: No tags file | @@ -306,4 +426,35 @@ describe('Mouse input', function() | ]]) end) + + it('horizontal scrolling', function() + feed("<esc>:set nowrap<cr>") + + feed("a <esc>20Ab<esc>") + screen:expect([[ + | + | + bbbbbbbbbbbbbbb^b | + ~ | + | + ]]) + + feed("<ScrollWheelLeft><0,0>") + screen:expect([[ + | + | + n bbbbbbbbbbbbbbbbbbb^b | + ~ | + | + ]]) + + feed("^<ScrollWheelRight><0,0>") + screen:expect([[ + g | + | + ^t and selection bbbbbbbbb| + ~ | + | + ]]) + end) end) diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua index c767f9b83a..a11fab18a2 100644 --- a/test/functional/ui/screen.lua +++ b/test/functional/ui/screen.lua @@ -106,8 +106,8 @@ -- use `screen:snapshot_util({},true)` local helpers = require('test.functional.helpers') -local request, run, stop = helpers.request, helpers.run, helpers.stop -local eq, dedent = helpers.eq, helpers.dedent +local request, run = helpers.request, helpers.run +local dedent = helpers.dedent local Screen = {} Screen.__index = Screen @@ -119,7 +119,7 @@ if os.getenv('VALGRIND') then default_screen_timeout = default_screen_timeout * 3 end -if os.getenv('CI_TARGET') then +if os.getenv('CI') then default_screen_timeout = default_screen_timeout * 3 end @@ -138,7 +138,7 @@ do -- this is just a helper to get any canonical name of a color colornames[rgb] = name end - session:exit(0) + session:close() Screen.colors = colors Screen.colornames = colornames end @@ -219,12 +219,23 @@ function Screen:expect(expected, attr_ids, attr_ignore) local ids = attr_ids or self._default_attr_ids local ignore = attr_ignore or self._default_attr_ignore self:wait(function() + local actual_rows = {} for i = 1, self._height do - local expected_row = expected_rows[i] - local actual_row = self:_row_repr(self._rows[i], ids, ignore) - if expected_row ~= actual_row then - return 'Row '..tostring(i)..' didn\'t match.\nExpected: "'.. - expected_row..'"\nActual: "'..actual_row..'"' + actual_rows[i] = self:_row_repr(self._rows[i], ids, ignore) + end + for i = 1, self._height do + if expected_rows[i] ~= actual_rows[i] then + local msg_expected_rows = {} + for j = 1, #expected_rows do + msg_expected_rows[j] = expected_rows[j] + end + msg_expected_rows[i] = '*' .. msg_expected_rows[i] + actual_rows[i] = '*' .. actual_rows[i] + return ( + 'Row ' .. tostring(i) .. ' didn\'t match.\n' + .. 'Expected:\n|' .. table.concat(msg_expected_rows, '|\n|') .. '|\n' + .. 'Actual:\n|' .. table.concat(actual_rows, '|\n|') .. '|' + ) end end end) @@ -241,7 +252,7 @@ function Screen:wait(check, timeout) checked = true if not err then success_seen = true - stop() + helpers.stop() elseif success_seen and #args > 0 then failure_after_success = true --print(require('inspect')(args)) @@ -294,9 +305,9 @@ end function Screen:_handle_resize(width, height) local rows = {} - for i = 1, height do + for _ = 1, height do local cols = {} - for j = 1, width do + for _ = 1, width do table.insert(cols, {text = ' ', attrs = {}}) end table.insert(rows, cols) @@ -448,7 +459,7 @@ function Screen:_row_repr(row, attr_ids, attr_ignore) local rv = {} local current_attr_id for i = 1, self._width do - local attr_id = get_attr_id(attr_ids, attr_ignore, row[i].attrs) + local attr_id = self:_get_attr_id(attr_ids, attr_ignore, row[i].attrs) if current_attr_id and attr_id ~= current_attr_id then -- close current attribute bracket, add it before any whitespace -- up to the current cell @@ -524,8 +535,8 @@ function Screen:print_snapshot(attrs, ignore) local row = self._rows[i] for j = 1, self._width do local attr = row[j].attrs - if attr_index(attrs, attr) == nil and attr_index(ignore, attr) == nil then - if not equal_attrs(attr, {}) then + if self:_attr_index(attrs, attr) == nil and self:_attr_index(ignore, attr) == nil then + if not self:_equal_attrs(attr, {}) then table.insert(attrs, attr) end end @@ -544,7 +555,7 @@ function Screen:print_snapshot(attrs, ignore) if self._default_attr_ids == nil or self._default_attr_ids[i] ~= a then alldefault = false end - local dict = "{"..pprint_attrs(a).."}" + local dict = "{"..self:_pprint_attrs(a).."}" table.insert(attrstrs, "["..tostring(i).."] = "..dict) end local attrstr = "{"..table.concat(attrstrs, ", ").."}" @@ -558,7 +569,7 @@ function Screen:print_snapshot(attrs, ignore) io.stdout:flush() end -function pprint_attrs(attrs) +function Screen:_pprint_attrs(attrs) local items = {} for f, v in pairs(attrs) do local desc = tostring(v) @@ -572,7 +583,7 @@ function pprint_attrs(attrs) return table.concat(items, ", ") end -function backward_find_meaningful(tbl, from) +function backward_find_meaningful(tbl, from) -- luacheck: ignore for i = from or #tbl, 1, -1 do if tbl[i] ~= ' ' then return i + 1 @@ -581,24 +592,24 @@ function backward_find_meaningful(tbl, from) return from end -function get_attr_id(attr_ids, ignore, attrs) +function Screen:_get_attr_id(attr_ids, ignore, attrs) if not attr_ids then return end for id, a in pairs(attr_ids) do - if equal_attrs(a, attrs) then + if self:_equal_attrs(a, attrs) then return id end end - if equal_attrs(attrs, {}) or - ignore == true or attr_index(ignore, attrs) ~= nil then + if self:_equal_attrs(attrs, {}) or + ignore == true or self:_attr_index(ignore, attrs) ~= nil then -- ignore this attrs return nil end - return "UNEXPECTED "..pprint_attrs(attrs) + return "UNEXPECTED "..self:_pprint_attrs(attrs) end -function equal_attrs(a, b) +function Screen:_equal_attrs(a, b) return a.bold == b.bold and a.standout == b.standout and a.underline == b.underline and a.undercurl == b.undercurl and a.italic == b.italic and a.reverse == b.reverse and @@ -606,12 +617,12 @@ function equal_attrs(a, b) a.background == b.background end -function attr_index(attrs, attr) +function Screen:_attr_index(attrs, attr) if not attrs then return nil end for i,a in pairs(attrs) do - if equal_attrs(a, attr) then + if self:_equal_attrs(a, attr) then return i end end diff --git a/test/functional/ui/screen_basic_spec.lua b/test/functional/ui/screen_basic_spec.lua index 092cc8c126..a4545eeff0 100644 --- a/test/functional/ui/screen_basic_spec.lua +++ b/test/functional/ui/screen_basic_spec.lua @@ -2,7 +2,7 @@ local helpers = require('test.functional.helpers') local Screen = require('test.functional.ui.screen') local spawn, set_session, clear = helpers.spawn, helpers.set_session, helpers.clear local feed, execute = helpers.feed, helpers.execute -local insert, wait = helpers.insert, helpers.wait +local insert = helpers.insert describe('Initial screen', function() local screen @@ -11,9 +11,6 @@ describe('Initial screen', function() '--embed'} before_each(function() - if session then - session:exit(0) - end local screen_nvim = spawn(nvim_argv) set_session(screen_nvim) screen = Screen.new() diff --git a/test/functional/ui/searchhl_spec.lua b/test/functional/ui/searchhl_spec.lua index d04329e1e2..e4217abcfe 100644 --- a/test/functional/ui/searchhl_spec.lua +++ b/test/functional/ui/searchhl_spec.lua @@ -1,7 +1,7 @@ local helpers = require('test.functional.helpers') local Screen = require('test.functional.ui.screen') -local clear, feed, nvim, insert = helpers.clear, helpers.feed, helpers.nvim, helpers.insert -local execute, request, eq = helpers.execute, helpers.request, helpers.eq +local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert +local execute = helpers.execute describe('search highlighting', function() local screen diff --git a/test/functional/ui/wildmode_spec.lua b/test/functional/ui/wildmode_spec.lua index de2f3e469d..c57d4abcbf 100644 --- a/test/functional/ui/wildmode_spec.lua +++ b/test/functional/ui/wildmode_spec.lua @@ -1,6 +1,7 @@ local helpers = require('test.functional.helpers') local Screen = require('test.functional.ui.screen') local clear, feed, execute = helpers.clear, helpers.feed, helpers.execute +local funcs = helpers.funcs describe("'wildmode'", function() local screen @@ -30,3 +31,34 @@ describe("'wildmode'", function() end) end) end) + +describe('command line completion', function() + local screen + + before_each(function() + clear() + screen = Screen.new(40, 5) + screen:attach() + screen:set_default_attr_ignore({{bold=true, foreground=Screen.colors.Blue}}) + end) + + after_each(function() + os.remove('Xtest-functional-viml-compl-dir') + end) + + it('lists directories with empty PATH', function() + local tmp = funcs.tempname() + execute('e '.. tmp) + execute('cd %:h') + execute("call mkdir('Xtest-functional-viml-compl-dir')") + execute('let $PATH=""') + feed(':!<tab><bs>') + screen:expect([[ + | + ~ | + ~ | + ~ | + :!Xtest-functional-viml-compl-dir^ | + ]]) + end) +end) diff --git a/test/functional/viml/completion_spec.lua b/test/functional/viml/completion_spec.lua index 5e3d4a6658..2b3844bf6d 100644 --- a/test/functional/viml/completion_spec.lua +++ b/test/functional/viml/completion_spec.lua @@ -1,12 +1,26 @@ - local helpers = require('test.functional.helpers') -local clear, feed, execute = helpers.clear, helpers.feed, helpers.execute +local Screen = require('test.functional.ui.screen') +local clear, feed = helpers.clear, helpers.feed local eval, eq, neq = helpers.eval, helpers.eq, helpers.neq -local execute, source = helpers.execute, helpers.source +local execute, source, expect = helpers.execute, helpers.source, helpers.expect describe('completion', function() + local screen + before_each(function() clear() + screen = Screen.new(60, 8) + screen:attach() + screen:set_default_attr_ignore({{bold=true, foreground=Screen.colors.Blue}}) + screen:set_default_attr_ids({ + [1] = {background = Screen.colors.LightMagenta}, + [2] = {background = Screen.colors.Grey}, + [3] = {bold = true}, + [4] = {bold = true, foreground = Screen.colors.SeaGreen}, + [5] = {foreground = Screen.colors.Red}, + [6] = {background = Screen.colors.Black}, + [7] = {foreground = Screen.colors.White, background = Screen.colors.Red}, + }) end) describe('v:completed_item', function() @@ -14,18 +28,40 @@ describe('completion', function() eq({}, eval('v:completed_item')) end) it('is empty dict if the candidate is not inserted', function() - feed('ifoo<ESC>o<C-x><C-n><C-e><ESC>') + feed('ifoo<ESC>o<C-x><C-n>') + screen:expect([[ + foo | + foo^ | + ~ | + ~ | + ~ | + ~ | + ~ | + {3:-- Keyword Local completion (^N^P) The only match} | + ]]) + feed('<C-e>') + screen:expect([[ + foo | + ^ | + ~ | + ~ | + ~ | + ~ | + ~ | + {3:-- INSERT --} | + ]]) + feed('<ESC>') eq({}, eval('v:completed_item')) end) it('returns expected dict in normal completion', function() - feed('ifoo<ESC>o<C-x><C-n><ESC>') + feed('ifoo<ESC>o<C-x><C-n>') eq('foo', eval('getline(2)')) eq({word = 'foo', abbr = '', menu = '', info = '', kind = ''}, eval('v:completed_item')) end) it('is readonly', function() + screen:try_resize(80, 8) feed('ifoo<ESC>o<C-x><C-n><ESC>') - execute('let v:completed_item.word = "bar"') neq(nil, string.find(eval('v:errmsg'), '^E46: ')) execute('let v:errmsg = ""') @@ -50,17 +86,29 @@ describe('completion', function() source([[ function! TestOmni(findstart, base) abort return a:findstart ? 0 : [{'word': 'foo', 'abbr': 'bar', - \ 'menu': 'baz', 'info': 'foobar', 'kind': 'foobaz'}] + \ 'menu': 'baz', 'info': 'foobar', 'kind': 'foobaz'}, + \ {'word': 'word', 'abbr': 'abbr', 'menu': 'menu', 'info': 'info', 'kind': 'kind'}] endfunction setlocal omnifunc=TestOmni ]]) - feed('i<C-x><C-o><ESC>') + feed('i<C-x><C-o>') eq('foo', eval('getline(1)')) + screen:expect([[ + foo^ | + {2:bar foobaz baz } | + {1:abbr kind menu } | + ~ | + ~ | + ~ | + ~ | + {3:-- Omni completion (^O^N^P) }{4:match 1 of 2} | + ]]) eq({word = 'foo', abbr = 'bar', menu = 'baz', info = 'foobar', kind = 'foobaz'}, eval('v:completed_item')) end) end) + describe('completeopt', function() before_each(function() source([[ @@ -73,31 +121,644 @@ describe('completion', function() it('inserts the first candidate if default', function() execute('set completeopt+=menuone') - feed('ifoo<ESC>o<C-x><C-n>bar<ESC>') + feed('ifoo<ESC>o') + screen:expect([[ + foo | + ^ | + ~ | + ~ | + ~ | + ~ | + ~ | + {3:-- INSERT --} | + ]]) + feed('<C-x>') + -- the ^X prompt, only test this once + screen:expect([[ + foo | + ^ | + ~ | + ~ | + ~ | + ~ | + ~ | + {3:-- ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)} | + ]]) + feed('<C-n>') + screen:expect([[ + foo | + foo^ | + {2:foo } | + ~ | + ~ | + ~ | + ~ | + {3:-- Keyword Local completion (^N^P) The only match} | + ]]) + feed('bar<ESC>') eq('foobar', eval('getline(2)')) - feed('o<C-r>=TestComplete()<CR><ESC>') + feed('o<C-r>=TestComplete()<CR>') + screen:expect([[ + foo | + foobar | + foo^ | + {2:foo } | + ~ | + ~ | + ~ | + {3:-- INSERT --} | + ]]) eq('foo', eval('getline(3)')) end) it('selects the first candidate if noinsert', function() execute('set completeopt+=menuone,noinsert') - feed('ifoo<ESC>o<C-x><C-n><C-y><ESC>') + feed('ifoo<ESC>o<C-x><C-n>') + screen:expect([[ + foo | + ^ | + {2:foo } | + ~ | + ~ | + ~ | + ~ | + {3:-- Keyword Local completion (^N^P) The only match} | + ]]) + feed('<C-y>') + screen:expect([[ + foo | + foo^ | + ~ | + ~ | + ~ | + ~ | + ~ | + {3:-- INSERT --} | + ]]) + feed('<ESC>') eq('foo', eval('getline(2)')) - feed('o<C-r>=TestComplete()<CR><C-y><ESC>') + feed('o<C-r>=TestComplete()<CR>') + screen:expect([[ + foo | + foo | + ^ | + {2:foo } | + ~ | + ~ | + ~ | + {3:-- INSERT --} | + ]]) + feed('<C-y><ESC>') eq('foo', eval('getline(3)')) end) it('does not insert the first candidate if noselect', function() execute('set completeopt+=menuone,noselect') - feed('ifoo<ESC>o<C-x><C-n>bar<ESC>') + feed('ifoo<ESC>o<C-x><C-n>') + screen:expect([[ + foo | + ^ | + {1:foo } | + ~ | + ~ | + ~ | + ~ | + {3:-- Keyword Local completion (^N^P) }{5:Back at original} | + ]]) + feed('b') + screen:expect([[ + foo | + b^ | + ~ | + ~ | + ~ | + ~ | + ~ | + {3:-- Keyword Local completion (^N^P) }{5:Back at original} | + ]]) + feed('ar<ESC>') eq('bar', eval('getline(2)')) - feed('o<C-r>=TestComplete()<CR>bar<ESC>') + feed('o<C-r>=TestComplete()<CR>') + screen:expect([[ + foo | + bar | + ^ | + {1:foo } | + ~ | + ~ | + ~ | + {3:-- INSERT --} | + ]]) + feed('bar<ESC>') eq('bar', eval('getline(3)')) end) it('does not select/insert the first candidate if noselect and noinsert', function() execute('set completeopt+=menuone,noselect,noinsert') - feed('ifoo<ESC>o<C-x><C-n><ESC>') + feed('ifoo<ESC>o<C-x><C-n>') + screen:expect([[ + foo | + ^ | + {1:foo } | + ~ | + ~ | + ~ | + ~ | + {3:-- Keyword Local completion (^N^P) }{5:Back at original} | + ]]) + feed('<ESC>') + screen:expect([[ + foo | + ^ | + ~ | + ~ | + ~ | + ~ | + ~ | + | + ]]) eq('', eval('getline(2)')) - feed('o<C-r>=TestComplete()<CR><ESC>') + feed('o<C-r>=TestComplete()<CR>') + screen:expect([[ + foo | + | + ^ | + {1:foo } | + ~ | + ~ | + ~ | + {3:-- INSERT --} | + ]]) + feed('<ESC>') + screen:expect([[ + foo | + | + ^ | + ~ | + ~ | + ~ | + ~ | + | + ]]) eq('', eval('getline(3)')) end) + it('does not change modified state if noinsert', function() + execute('set completeopt+=menuone,noinsert') + execute('setlocal nomodified') + feed('i<C-r>=TestComplete()<CR><ESC>') + eq(0, eval('&l:modified')) + end) + it('does not change modified state if noselect', function() + execute('set completeopt+=menuone,noselect') + execute('setlocal nomodified') + feed('i<C-r>=TestComplete()<CR><ESC>') + eq(0, eval('&l:modified')) + end) + end) + + describe("refresh:always", function() + before_each(function() + source([[ + function! TestCompletion(findstart, base) abort + if a:findstart + let line = getline('.') + let start = col('.') - 1 + while start > 0 && line[start - 1] =~ '\a' + let start -= 1 + endwhile + return start + else + let ret = [] + for m in split("January February March April May June July August September October November December") + if m =~ a:base " match by regex + call add(ret, m) + endif + endfor + return {'words':ret, 'refresh':'always'} + endif + endfunction + + set completeopt=menuone,noselect + set completefunc=TestCompletion + ]]) + end ) + + it('completes on each input char', function () + feed('i<C-x><C-u>') + screen:expect([[ + ^ | + {1:January }{6: } | + {1:February }{6: } | + {1:March }{6: } | + {1:April }{2: } | + {1:May }{2: } | + {1:June }{2: } | + {3:-- User defined completion (^U^N^P) }{5:Back at original} | + ]]) + feed('u') + screen:expect([[ + u^ | + {1:January } | + {1:February } | + {1:June } | + {1:July } | + {1:August } | + ~ | + {3:-- User defined completion (^U^N^P) }{5:Back at original} | + ]]) + feed('g') + screen:expect([[ + ug^ | + {1:August } | + ~ | + ~ | + ~ | + ~ | + ~ | + {3:-- User defined completion (^U^N^P) }{5:Back at original} | + ]]) + feed('<Down>') + screen:expect([[ + ug^ | + {2:August } | + ~ | + ~ | + ~ | + ~ | + ~ | + {3:-- User defined completion (^U^N^P) The only match} | + ]]) + feed('<C-y>') + screen:expect([[ + August^ | + ~ | + ~ | + ~ | + ~ | + ~ | + ~ | + {3:-- INSERT --} | + ]]) + expect('August') + end) + it("repeats correctly after backspace #2674", function () + feed('o<C-x><C-u>Ja') + screen:expect([[ + | + Ja^ | + {1:January } | + ~ | + ~ | + ~ | + ~ | + {3:-- User defined completion (^U^N^P) }{5:Back at original} | + ]]) + feed('<BS>') + screen:expect([[ + | + J^ | + {1:January } | + {1:June } | + {1:July } | + ~ | + ~ | + {3:-- User defined completion (^U^N^P) }{5:Back at original} | + ]]) + feed('<C-n>') + screen:expect([[ + | + January^ | + {2:January } | + {1:June } | + {1:July } | + ~ | + ~ | + {3:-- User defined completion (^U^N^P) }{4:match 1 of 3} | + ]]) + feed('<C-n>') + screen:expect([[ + | + June^ | + {1:January } | + {2:June } | + {1:July } | + ~ | + ~ | + {3:-- User defined completion (^U^N^P) }{4:match 2 of 3} | + ]]) + feed('<Esc>') + screen:expect([[ + | + Jun^e | + ~ | + ~ | + ~ | + ~ | + ~ | + | + ]]) + feed('.') + screen:expect([[ + | + June | + Jun^e | + ~ | + ~ | + ~ | + ~ | + | + ]]) + expect([[ + + June + June]]) + end) + end) + + describe('with a lot of items', function() + before_each(function() + source([[ + function! TestComplete() abort + call complete(1, map(range(0,100), "string(v:val)")) + return '' + endfunction + ]]) + execute("set completeopt=menuone,noselect") + end) + + it("works", function() + feed('i<C-r>=TestComplete()<CR>') + screen:expect([[ + ^ | + {1:0 }{6: } | + {1:1 }{2: } | + {1:2 }{2: } | + {1:3 }{2: } | + {1:4 }{2: } | + {1:5 }{2: } | + {3:-- INSERT --} | + ]]) + feed('7') + screen:expect([[ + 7^ | + {1:7 }{6: } | + {1:70 }{6: } | + {1:71 }{6: } | + {1:72 }{2: } | + {1:73 }{2: } | + {1:74 }{2: } | + {3:-- INSERT --} | + ]]) + feed('<c-n>') + screen:expect([[ + 7^ | + {2:7 }{6: } | + {1:70 }{6: } | + {1:71 }{6: } | + {1:72 }{2: } | + {1:73 }{2: } | + {1:74 }{2: } | + {3:-- INSERT --} | + ]]) + feed('<c-n>') + screen:expect([[ + 70^ | + {1:7 }{6: } | + {2:70 }{6: } | + {1:71 }{6: } | + {1:72 }{2: } | + {1:73 }{2: } | + {1:74 }{2: } | + {3:-- INSERT --} | + ]]) + end) + + it('can be navigated with <PageDown>, <PageUp>', function() + feed('i<C-r>=TestComplete()<CR>') + screen:expect([[ + ^ | + {1:0 }{6: } | + {1:1 }{2: } | + {1:2 }{2: } | + {1:3 }{2: } | + {1:4 }{2: } | + {1:5 }{2: } | + {3:-- INSERT --} | + ]]) + feed('<PageDown>') + screen:expect([[ + ^ | + {1:0 }{6: } | + {1:1 }{2: } | + {1:2 }{2: } | + {2:3 } | + {1:4 }{2: } | + {1:5 }{2: } | + {3:-- INSERT --} | + ]]) + feed('<PageDown>') + screen:expect([[ + ^ | + {1:5 }{6: } | + {1:6 }{2: } | + {2:7 } | + {1:8 }{2: } | + {1:9 }{2: } | + {1:10 }{2: } | + {3:-- INSERT --} | + ]]) + feed('<Down>') + screen:expect([[ + ^ | + {1:5 }{6: } | + {1:6 }{2: } | + {1:7 }{2: } | + {2:8 } | + {1:9 }{2: } | + {1:10 }{2: } | + {3:-- INSERT --} | + ]]) + feed('<PageUp>') + screen:expect([[ + ^ | + {1:2 }{6: } | + {1:3 }{2: } | + {2:4 } | + {1:5 }{2: } | + {1:6 }{2: } | + {1:7 }{2: } | + {3:-- INSERT --} | + ]]) + feed('<PageUp>') -- stop on first item + screen:expect([[ + ^ | + {2:0 }{6: } | + {1:1 }{2: } | + {1:2 }{2: } | + {1:3 }{2: } | + {1:4 }{2: } | + {1:5 }{2: } | + {3:-- INSERT --} | + ]]) + feed('<PageUp>') -- when on first item, unselect + screen:expect([[ + ^ | + {1:0 }{6: } | + {1:1 }{2: } | + {1:2 }{2: } | + {1:3 }{2: } | + {1:4 }{2: } | + {1:5 }{2: } | + {3:-- INSERT --} | + ]]) + feed('<PageUp>') -- when unselected, select last item + screen:expect([[ + ^ | + {1:95 }{2: } | + {1:96 }{2: } | + {1:97 }{2: } | + {1:98 }{2: } | + {1:99 }{2: } | + {2:100 }{6: } | + {3:-- INSERT --} | + ]]) + feed('<PageUp>') + screen:expect([[ + ^ | + {1:94 }{2: } | + {1:95 }{2: } | + {2:96 } | + {1:97 }{2: } | + {1:98 }{2: } | + {1:99 }{6: } | + {3:-- INSERT --} | + ]]) + feed('<cr>') + screen:expect([[ + 96^ | + ~ | + ~ | + ~ | + ~ | + ~ | + ~ | + {3:-- INSERT --} | + ]]) + end) + end) + + + it('disables folding during completion', function () + execute("set foldmethod=indent") + feed('i<Tab>foo<CR><Tab>bar<Esc>gg') + screen:expect([[ + ^foo | + bar | + ~ | + ~ | + ~ | + ~ | + ~ | + | + ]]) + feed('A<C-x><C-l>') + screen:expect([[ + foo^ | + bar | + ~ | + ~ | + ~ | + ~ | + ~ | + {3:-- Whole line completion (^L^N^P) }{7:Pattern not found} | + ]]) + eq(-1, eval('foldclosed(1)')) + end) + + it('popupmenu is not interrupted by events', function () + execute("set complete=.") + + feed('ifoobar fooegg<cr>f<c-p>') + screen:expect([[ + foobar fooegg | + fooegg^ | + {1:foobar } | + {2:fooegg } | + ~ | + ~ | + ~ | + {3:-- Keyword completion (^N^P) }{4:match 1 of 2} | + ]]) + + eval('1 + 1') + -- popupmenu still visible + screen:expect([[ + foobar fooegg | + fooegg^ | + {1:foobar } | + {2:fooegg } | + ~ | + ~ | + ~ | + {3:-- Keyword completion (^N^P) }{4:match 1 of 2} | + ]]) + + feed('<c-p>') + -- Didn't restart completion: old matches still used + screen:expect([[ + foobar fooegg | + foobar^ | + {2:foobar } | + {1:fooegg } | + ~ | + ~ | + ~ | + {3:-- Keyword completion (^N^P) }{4:match 2 of 2} | + ]]) + end) + + describe('from the commandline window', function() + + it('is cleared after CTRL-C', function () + feed('q:') + feed('ifoo faa fee f') + screen:expect([[ + | + {8:[No Name] }| + :foo faa fee f^ | + :~ | + :~ | + :~ | + {9:[Command Line] }| + {3:-- INSERT --} | + ]], {[3] = {bold = true}, + [4] = {bold = true, foreground = Screen.colors.SeaGreen}, + [8] = {reverse = true}, + [9] = {bold = true, reverse = true}}) + feed('<c-x><c-n>') + screen:expect([[ + | + {8:[No Name] }| + :foo faa fee foo^ | + :~ {2: foo } | + :~ {1: faa } | + :~ {1: fee } | + {9:[Command Line] }| + {3:-- Keyword Local completion (^N^P) }{4:match 1 of 3} | + ]],{[1] = {background = Screen.colors.LightMagenta}, + [2] = {background = Screen.colors.Grey}, + [3] = {bold = true}, + [4] = {bold = true, foreground = Screen.colors.SeaGreen}, + [8] = {reverse = true}, + [9] = {bold = true, reverse = true}}) + feed('<c-c>') + screen:expect([[ + | + {8:[No Name] }| + :foo faa fee foo | + :~ | + :~ | + :~ | + {9:[Command Line] }| + :foo faa fee foo^ | + ]], {[8] = {reverse = true}, [9] = {bold = true, reverse = true}}) + end) end) end) diff --git a/test/functional/viml/errorlist_spec.lua b/test/functional/viml/errorlist_spec.lua new file mode 100644 index 0000000000..30cb86f8d1 --- /dev/null +++ b/test/functional/viml/errorlist_spec.lua @@ -0,0 +1,73 @@ +local helpers = require('test.functional.helpers') + +local clear = helpers.clear +local command = helpers.command +local eq = helpers.eq +local exc_exec = helpers.exc_exec +local get_cur_win_var = helpers.curwinmeths.get_var + +describe('setqflist()', function() + local setqflist = helpers.funcs.setqflist + + before_each(clear) + + it('requires a list for {list}', function() + eq('Vim(call):E714: List required', exc_exec('call setqflist("foo")')) + eq('Vim(call):E714: List required', exc_exec('call setqflist(5)')) + eq('Vim(call):E714: List required', exc_exec('call setqflist({})')) + end) + + it('requires a string for {action}', function() + eq('Vim(call):E114: String required', exc_exec('call setqflist([], 5)')) + eq('Vim(call):E114: String required', exc_exec('call setqflist([], [])')) + eq('Vim(call):E114: String required', exc_exec('call setqflist([], {})')) + end) + + it('sets w:quickfix_title', function() + setqflist({''}, 'r', 'foo') + command('copen') + eq(':foo', get_cur_win_var('quickfix_title')) + end) + + it('requires string or number for {title}', function() + command('copen') + setqflist({}, 'r', '5') + eq(':5', get_cur_win_var('quickfix_title')) + setqflist({}, 'r', 6) + eq(':6', get_cur_win_var('quickfix_title')) + local exc = exc_exec('call setqflist([], "r", function("function"))') + eq('Vim(call):E729: using Funcref as a String', exc) + exc = exc_exec('call setqflist([], "r", [])') + eq('Vim(call):E730: using List as a String', exc) + exc = exc_exec('call setqflist([], "r", {})') + eq('Vim(call):E731: using Dictionary as a String', exc) + end) +end) + +describe('setloclist()', function() + local setloclist = helpers.funcs.setloclist + + before_each(clear) + + it('requires a list for {list}', function() + eq('Vim(call):E714: List required', exc_exec('call setloclist(0, "foo")')) + eq('Vim(call):E714: List required', exc_exec('call setloclist(0, 5)')) + eq('Vim(call):E714: List required', exc_exec('call setloclist(0, {})')) + end) + + it('requires a string for {action}', function() + eq('Vim(call):E114: String required', exc_exec('call setloclist(0, [], 5)')) + eq('Vim(call):E114: String required', exc_exec('call setloclist(0, [], [])')) + eq('Vim(call):E114: String required', exc_exec('call setloclist(0, [], {})')) + end) + + it('sets w:quickfix_title for the correct window', function() + command('rightbelow vsplit') + setloclist(1, {}, 'r', 'foo') + setloclist(2, {}, 'r', 'bar') + command('lopen') + eq(':bar', get_cur_win_var('quickfix_title')) + command('lclose | wincmd w | lopen') + eq(':foo', get_cur_win_var('quickfix_title')) + end) +end) diff --git a/test/functional/viml/function_spec.lua b/test/functional/viml/function_spec.lua new file mode 100644 index 0000000000..665f5d4467 --- /dev/null +++ b/test/functional/viml/function_spec.lua @@ -0,0 +1,29 @@ +local helpers = require('test.functional.helpers') + +local clear = helpers.clear +local eq = helpers.eq +local exc_exec = helpers.exc_exec + +describe('Up to MAX_FUNC_ARGS arguments are handled by', function() + local max_func_args = 20 -- from eval.h + local range = helpers.funcs.range + + before_each(clear) + + it('printf()', function() + local printf = helpers.funcs.printf + local rep = helpers.funcs['repeat'] + local expected = '2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,' + eq(expected, printf(rep('%d,', max_func_args-1), unpack(range(2, max_func_args)))) + local ret = exc_exec('call printf("", 2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21)') + eq('Vim(call):E740: Too many arguments for function printf', ret) + end) + + it('rpcnotify()', function() + local rpcnotify = helpers.funcs.rpcnotify + local ret = rpcnotify(0, 'foo', unpack(range(3, max_func_args))) + eq(1, ret) + ret = exc_exec('call rpcnotify(0, "foo", 3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21)') + eq('Vim(call):E740: Too many arguments for function rpcnotify', ret) + end) +end) |