diff options
Diffstat (limited to 'test/functional')
67 files changed, 4544 insertions, 653 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/server_requests_spec.lua b/test/functional/api/server_requests_spec.lua index c0099e44c4..1b33275803 100644 --- a/test/functional/api/server_requests_spec.lua +++ b/test/functional/api/server_requests_spec.lua @@ -165,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 9c9759adf6..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_is_windows = helpers.os_is_windows +local os_name = helpers.os_name +local meths = helpers.meths +local funcs = helpers.funcs describe('vim_* functions', function() before_each(clear) @@ -17,7 +20,7 @@ describe('vim_* functions', function() nvim('command', 'w') local f = io.open(fname) ok(f ~= nil) - if os_is_windows() then + if os_name() == 'windows' then eq('testing\r\napi\r\n', f:read('*a')) else eq('testing\napi\n', f:read('*a')) @@ -70,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')) diff --git a/test/functional/api/window_spec.lua b/test/functional/api/window_spec.lua index 17aacafe9b..92a33b4cdb 100644 --- a/test/functional/api/window_spec.lua +++ b/test/functional/api/window_spec.lua @@ -5,6 +5,8 @@ local clear, nvim, curbuf, curbuf_contents, window, curwin, eq, neq, helpers.curbuf_contents, helpers.window, helpers.curwin, helpers.eq, 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) @@ -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/tabnew_spec.lua b/test/functional/autocmd/tabnew_spec.lua index 5ab504889b..aaf9db0a99 100644 --- a/test/functional/autocmd/tabnew_spec.lua +++ b/test/functional/autocmd/tabnew_spec.lua @@ -1,22 +1,28 @@ local helpers = require('test.functional.helpers') -local clear, nvim, eq = helpers.clear, helpers.nvim, helpers.eq -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() - local 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/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/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 41b0faf76c..9e501353a5 100644 --- a/test/functional/eval/msgpack_functions_spec.lua +++ b/test/functional/eval/msgpack_functions_spec.lua @@ -1,5 +1,6 @@ local helpers = require('test.functional.helpers') local clear = helpers.clear +local funcs = helpers.funcs local eval, eq = helpers.eval, helpers.eq local execute = helpers.execute local nvim = helpers.nvim @@ -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() @@ -512,33 +515,55 @@ 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):E951: Error while dumping msgpackdump() argument, index 0, itself: attempt to dump function reference', @@ -654,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/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 649e9dbabe..7f53522c08 100644 --- a/test/functional/server/server_spec.lua +++ b/test/functional/eval/server_spec.lua @@ -2,7 +2,7 @@ local helpers = require('test.functional.helpers') 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_is_windows = helpers.os_is_windows +local os_name = helpers.os_name describe('serverstart(), serverstop()', function() before_each(clear) @@ -39,7 +39,7 @@ describe('serverstart(), serverstop()', function() eq('', meths.get_vvar('servername')) -- v:servername will take the next available server. - local servername = (os_is_windows() + local servername = (os_name() == 'windows' and [[\\.\pipe\Xtest-functional-server-server-pipe]] or 'Xtest-functional-server-server-socket') funcs.serverstart(servername) 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/oldfiles_spec.lua b/test/functional/ex_cmds/oldfiles_spec.lua index dac6757a97..5bba1a0e7c 100644 --- a/test/functional/ex_cmds/oldfiles_spec.lua +++ b/test/functional/ex_cmds/oldfiles_spec.lua @@ -2,7 +2,7 @@ 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 = helpers.feed, helpers.nvim_prog +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' @@ -59,9 +59,13 @@ describe(':oldfiles!', function() 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. 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 c3147e1c0f..969dfea3d9 100644 --- a/test/functional/ex_cmds/wundo_spec.lua +++ b/test/functional/ex_cmds/wundo_spec.lua @@ -24,6 +24,6 @@ describe('u_* functions', function() '-c', 'set undodir=. undofile'}) set_session(session) execute('echo "True"') -- Should not error out due to crashed Neovim - session:exit(0) + session:close() end) end) diff --git a/test/functional/ex_cmds/wviminfo_spec.lua b/test/functional/ex_cmds/wviminfo_spec.lua index 207d94124f..21f14be62c 100644 --- a/test/functional/ex_cmds/wviminfo_spec.lua +++ b/test/functional/ex_cmds/wviminfo_spec.lua @@ -9,7 +9,7 @@ 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. 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 1fba15c2c3..37b7bf664c 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -1,10 +1,7 @@ require('coxpcall') -local ffi = require('ffi') 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' @@ -12,6 +9,8 @@ local nvim_argv = {nvim_prog, '-u', 'NONE', '-i', 'NONE', '-N', '--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. @@ -60,6 +59,9 @@ end local session, loop_running, last_error local function set_session(s) + if session then + session:close() + end session = s 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 sess = Session.new(async_session) - loop:spawn(merge and merge_args(prepend_argv, argv) or argv) - return sess + 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,7 +258,7 @@ end local function source(code) local tmpname = os.tmpname() - if ffi.os == 'OSX' and string.match(tmpname, '^/tmp') then + if os_name() == 'osx' and string.match(tmpname, '^/tmp') then tmpname = '/private'..tmpname end write_file(tmpname, code) @@ -305,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, ...) @@ -328,24 +339,18 @@ local function expect(contents) return eq(dedent(contents), curbuf_contents()) end -local function os_is_windows() - return nvim_eval('has("win32")') == 1 -end - local function rmdir(path) if lfs.attributes(path, 'mode') ~= 'directory' then return nil end for file in lfs.dir(path) do - if file == '.' or file == '..' then - goto continue - end - local 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 local ret, err = os.remove(path) if not ret then @@ -434,7 +439,7 @@ return { wait = wait, set_session = set_session, write_file = write_file, - os_is_windows = os_is_windows, + os_name = os_name, rmdir = rmdir, mkdir = lfs.mkdir, exc_exec = exc_exec, @@ -448,4 +453,5 @@ return { curbufmeths = curbufmeths, curwinmeths = curwinmeths, curtabmeths = curtabmeths, + NIL = mpack.NIL } diff --git a/test/functional/legacy/036_regexp_character_classes_spec.lua b/test/functional/legacy/036_regexp_character_classes_spec.lua index 3c264423ff..de080f4b43 100644 --- a/test/functional/legacy/036_regexp_character_classes_spec.lua +++ b/test/functional/legacy/036_regexp_character_classes_spec.lua @@ -1,9 +1,9 @@ -- Test character classes in regexp using regexpengine 0, 1, 2. local helpers = require('test.functional.helpers') -local ffi = require('ffi') 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 = '' @@ -15,7 +15,7 @@ end local function diff(text, nodedent) local tmpname = os.tmpname() - if ffi.os == 'OSX' and string.match(tmpname, '^/tmp') then + if os_name() == 'osx' and string.match(tmpname, '^/tmp') then tmpname = '/private'..tmpname end execute('w! '..tmpname) @@ -30,7 +30,7 @@ local function diff(text, nodedent) end describe('character classes in regexp', function() - local ctrl1 = '\t\x0c\r' + local ctrl1 = '\t\012\r' local punct1 = " !\"#$%&'()#+'-./" local digits = '0123456789' local punct2 = ':;<=>?@' @@ -38,8 +38,8 @@ describe('character classes in regexp', function() local punct3 = '[\\]^_`' local lower = 'abcdefghiwxyz' local punct4 = '{|}~' - local ctrl2 = '\x7f\x80\x82\x90\x9b' - local iso_text = '\xa6\xb1\xbc\xc7\xd3\xe9' -- "¦±¼ÇÓé" in utf-8 + 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 diff --git a/test/functional/legacy/039_visual_block_mode_commands_spec.lua b/test/functional/legacy/039_visual_block_mode_commands_spec.lua index 6e1879035b..7195d7d11d 100644 --- a/test/functional/legacy/039_visual_block_mode_commands_spec.lua +++ b/test/functional/legacy/039_visual_block_mode_commands_spec.lua @@ -187,7 +187,7 @@ describe('Visual block mode', function() 98<Nul>65 98<Nul>65]] expected = expected:gsub('<CR>', '\r') - expected = expected:gsub('<Nul>', '\x00') + expected = expected:gsub('<Nul>', '\000') expect(expected) 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 1359b45228..2a4c0149fa 100644 --- a/test/functional/legacy/044_099_regexp_multibyte_magic_spec.lua +++ b/test/functional/legacy/044_099_regexp_multibyte_magic_spec.lua @@ -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/057_sort_spec.lua b/test/functional/legacy/057_sort_spec.lua index 7eed31e292..36062ded3a 100644 --- a/test/functional/legacy/057_sort_spec.lua +++ b/test/functional/legacy/057_sort_spec.lua @@ -668,4 +668,22 @@ describe(':sort', function() 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/061_undo_tree_spec.lua b/test/functional/legacy/061_undo_tree_spec.lua index ceb114b2a7..6db37bf1ff 100644 --- a/test/functional/legacy/061_undo_tree_spec.lua +++ b/test/functional/legacy/061_undo_tree_spec.lua @@ -191,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/083_tag_search_with_file_encoding_spec.lua b/test/functional/legacy/083_tag_search_with_file_encoding_spec.lua index dc6df007e6..6b5ee60568 100644 --- a/test/functional/legacy/083_tag_search_with_file_encoding_spec.lua +++ b/test/functional/legacy/083_tag_search_with_file_encoding_spec.lua @@ -24,7 +24,7 @@ describe('tag search with !_TAG_FILE_ENCODING', function() ]]) write_file('test83-tags2', '!_TAG_FILE_ENCODING cp932 //\n' .. - '\x82`\x82a\x82b Xtags2.txt /\x82`\x82a\x82b\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. @@ -32,7 +32,7 @@ describe('tag search with !_TAG_FILE_ENCODING', function() !_TAG_FILE_SORTED 1 // !_TAG_FILE_ENCODING cp932 // ]]) - local line = ' Xtags3.txt /\x82`\x82a\x82b\n' + local line = ' Xtags3.txt /\130`\130a\130b\n' for i = 1, 100 do text = text .. 'abc' .. i .. line end 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/100_lispwords_spec.lua b/test/functional/legacy/100_lispwords_spec.lua deleted file mode 100644 index 739a02f0b3..0000000000 --- a/test/functional/legacy/100_lispwords_spec.lua +++ /dev/null @@ -1,47 +0,0 @@ --- Tests for 'lispwords' setting being global-local - -local helpers = require('test.functional.helpers') -local source = helpers.source -local clear, expect = helpers.clear, helpers.expect - -describe('lispwords', function() - setup(clear) - - it('global-local', function() - source([[ - 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([[ - - 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/105_filename_modifiers_spec.lua b/test/functional/legacy/105_filename_modifiers_spec.lua deleted file mode 100644 index 3413667022..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 = helpers.clear -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/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/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/eval_spec.lua b/test/functional/legacy/eval_spec.lua index 1c81b47ed6..3ff1092a4b 100644 --- a/test/functional/legacy/eval_spec.lua +++ b/test/functional/legacy/eval_spec.lua @@ -54,8 +54,8 @@ describe('eval', function() expect([[ ": type v; value: abc (['abc']), expr: abc (['abc']) - ": type V; value: abc]].."\x00 (['abc']), expr: abc\x00"..[[ (['abc']) - ": type V; value: abc]].."\r\x00 (['abc\r']), expr: abc\r\x00 (['abc\r"..[[']) + ": 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) @@ -97,29 +97,29 @@ describe('eval', function() == =abcB= {{{2 setreg('c', 'abcC', 'l') - c: type V; value: abcC]].."\x00 (['abcC']), expr: abcC\x00"..[[ (['abcC']) + c: type V; value: abcC]].."\000 (['abcC']), expr: abcC\000"..[[ (['abcC']) == abcC == {{{2 setreg('d', 'abcD', 'V') - d: type V; value: abcD]].."\x00 (['abcD']), expr: abcD\x00"..[[ (['abcD']) + d: type V; value: abcD]].."\000 (['abcD']), expr: abcD\000"..[[ (['abcD']) == abcD == {{{2 setreg('e', 'abcE', 'b') - e: type ]]..'\x16'..[[4; value: abcE (['abcE']), expr: abcE (['abcE']) + e: type ]]..'\022'..[[4; value: abcE (['abcE']), expr: abcE (['abcE']) == =abcE= - {{{2 setreg('f', 'abcF', ']]..'\x16'..[[') - f: type ]]..'\x16'..[[4; value: abcF (['abcF']), expr: abcF (['abcF']) + {{{2 setreg('f', 'abcF', ']]..'\022'..[[') + f: type ]]..'\022'..[[4; value: abcF (['abcF']), expr: abcF (['abcF']) == =abcF= {{{2 setreg('g', 'abcG', 'b10') - g: type ]]..'\x16'..[[10; value: abcG (['abcG']), expr: abcG (['abcG']) + g: type ]]..'\022'..[[10; value: abcG (['abcG']), expr: abcG (['abcG']) == =abcG = - {{{2 setreg('h', 'abcH', ']]..'\x16'..[[10') - h: type ]]..'\x16'..[[10; value: abcH (['abcH']), expr: abcH (['abcH']) + {{{2 setreg('h', 'abcH', ']]..'\022'..[[10') + h: type ]]..'\022'..[[10; value: abcH (['abcH']), expr: abcH (['abcH']) == =abcH = {{{2 setreg('I', 'abcI') @@ -132,12 +132,12 @@ describe('eval', function() == =abcAabcAc= {{{2 setreg('A', 'abcAl', 'l') - A: type V; value: abcAabcAcabcAl]].."\x00 (['abcAabcAcabcAl']), expr: abcAabcAcabcAl\x00"..[[ (['abcAabcAcabcAl']) + A: type V; value: abcAabcAcabcAl]].."\000 (['abcAabcAcabcAl']), expr: abcAabcAcabcAl\000"..[[ (['abcAabcAcabcAl']) == abcAabcAcabcAl == {{{2 setreg('A', 'abcAc2', 'c') - A: type v; value: abcAabcAcabcAl]].."\x00abcAc2 (['abcAabcAcabcAl', 'abcAc2']), expr: abcAabcAcabcAl\x00"..[[abcAc2 (['abcAabcAcabcAl', 'abcAc2']) + A: type v; value: abcAabcAcabcAl]].."\000abcAc2 (['abcAabcAcabcAl', 'abcAc2']), expr: abcAabcAcabcAl\000"..[[abcAc2 (['abcAabcAcabcAl', 'abcAc2']) == =abcAabcAcabcAl abcAc2= @@ -146,50 +146,50 @@ describe('eval', function() == =abcBabcBc= {{{2 setreg('b', 'abcBb', 'ba') - b: type ]]..'\x16'..[[5; value: abcBabcBcabcBb (['abcBabcBcabcBb']), expr: abcBabcBcabcBb (['abcBabcBcabcBb']) + b: type ]]..'\022'..[[5; value: abcBabcBcabcBb (['abcBabcBcabcBb']), expr: abcBabcBcabcBb (['abcBabcBcabcBb']) == =abcBabcBcabcBb= {{{2 setreg('b', 'abcBc2', 'ca') - b: type v; value: abcBabcBcabcBb]].."\x00abcBc2 (['abcBabcBcabcBb', 'abcBc2']), expr: abcBabcBcabcBb\x00"..[[abcBc2 (['abcBabcBcabcBb', 'abcBc2']) + b: type v; value: abcBabcBcabcBb]].."\000abcBc2 (['abcBabcBcabcBb', 'abcBc2']), expr: abcBabcBcabcBb\000"..[[abcBc2 (['abcBabcBcabcBb', 'abcBc2']) == =abcBabcBcabcBb abcBc2= {{{2 setreg('b', 'abcBb2', 'b50a') - b: type ]].."\x1650; value: abcBabcBcabcBb\x00abcBc2abcBb2 (['abcBabcBcabcBb', 'abcBc2abcBb2']), expr: abcBabcBcabcBb\x00"..[[abcBc2abcBb2 (['abcBabcBcabcBb', 'abcBc2abcBb2']) + 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]].."\x00abcCl\x00 (['abcC', 'abcCl']), expr: abcC\x00abcCl\x00"..[[ (['abcC', 'abcCl']) + 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]].."\x00abcCl\x00abcCc (['abcC', 'abcCl', 'abcCc']), expr: abcC\x00abcCl\x00"..[[abcCc (['abcC', 'abcCl', 'abcCc']) + 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 ]].."\x165; value: abcD\x00abcDb (['abcD', 'abcDb']), expr: abcD\x00"..[[abcDb (['abcD', 'abcDb']) + D: type ]].."\0225; value: abcD\000abcDb (['abcD', 'abcDb']), expr: abcD\000"..[[abcDb (['abcD', 'abcDb']) == =abcD = abcDb {{{2 setreg('E', 'abcEb', 'b') - E: type ]].."\x165; value: abcE\x00abcEb (['abcE', 'abcEb']), expr: abcE\x00"..[[abcEb (['abcE', 'abcEb']) + 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]].."\x00abcEb\x00abcEl\x00 (['abcE', 'abcEb', 'abcEl']), expr: abcE\x00abcEb\x00abcEl\x00"..[[ (['abcE', 'abcEb', 'abcEl']) + 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]].."\x00abcFc (['abcF', 'abcFc']), expr: abcF\x00"..[[abcFc (['abcF', 'abcFc']) + F: type v; value: abcF]].."\000abcFc (['abcF', 'abcFc']), expr: abcF\000"..[[abcFc (['abcF', 'abcFc']) == =abcF abcFc=]]) @@ -219,36 +219,36 @@ describe('eval', function() execute([[call SetReg('F', "\n", 'b')]]) expect([[ - {{{2 setreg('A', ']]..'\x00'..[[') - A: type V; value: abcA2]].."\x00 (['abcA2']), expr: abcA2\x00"..[[ (['abcA2']) + {{{2 setreg('A', ']]..'\000'..[[') + A: type V; value: abcA2]].."\000 (['abcA2']), expr: abcA2\000"..[[ (['abcA2']) == abcA2 == - {{{2 setreg('B', ']]..'\x00'..[[', 'c') - B: type v; value: abcB2]].."\x00 (['abcB2', '']), expr: abcB2\x00"..[[ (['abcB2', '']) + {{{2 setreg('B', ']]..'\000'..[[', 'c') + B: type v; value: abcB2]].."\000 (['abcB2', '']), expr: abcB2\000"..[[ (['abcB2', '']) == =abcB2 = - {{{2 setreg('C', ']]..'\x00'..[[') - C: type V; value: abcC2]].."\x00\x00 (['abcC2', '']), expr: abcC2\x00\x00"..[[ (['abcC2', '']) + {{{2 setreg('C', ']]..'\000'..[[') + C: type V; value: abcC2]].."\000\000 (['abcC2', '']), expr: abcC2\000\000"..[[ (['abcC2', '']) == abcC2 == - {{{2 setreg('D', ']]..'\x00'..[[', 'l') - D: type V; value: abcD2]].."\x00\x00 (['abcD2', '']), expr: abcD2\x00\x00"..[[ (['abcD2', '']) + {{{2 setreg('D', ']]..'\000'..[[', 'l') + D: type V; value: abcD2]].."\000\000 (['abcD2', '']), expr: abcD2\000\000"..[[ (['abcD2', '']) == abcD2 == - {{{2 setreg('E', ']]..'\x00'..[[') - E: type V; value: abcE2]].."\x00\x00 (['abcE2', '']), expr: abcE2\x00\x00"..[[ (['abcE2', '']) + {{{2 setreg('E', ']]..'\000'..[[') + E: type V; value: abcE2]].."\000\000 (['abcE2', '']), expr: abcE2\000\000"..[[ (['abcE2', '']) == abcE2 == - {{{2 setreg('F', ']]..'\x00'..[[', 'b') - F: type ]].."\x160; value: abcF2\x00 (['abcF2', '']), expr: abcF2\x00"..[[ (['abcF2', '']) + {{{2 setreg('F', ']]..'\000'..[[', 'b') + F: type ]].."\0220; value: abcF2\000 (['abcF2', '']), expr: abcF2\000"..[[ (['abcF2', '']) == =abcF2= ]]) @@ -282,21 +282,21 @@ describe('eval', function() == =abcA3= {{{2 setreg('b', ['abcB3'], 'l') - b: type V; value: abcB3]].."\x00 (['abcB3']), expr: abcB3\x00"..[[ (['abcB3']) + b: type V; value: abcB3]].."\000 (['abcB3']), expr: abcB3\000"..[[ (['abcB3']) == abcB3 == {{{2 setreg('c', ['abcC3'], 'b') - c: type ]]..'\x16'..[[5; value: abcC3 (['abcC3']), expr: abcC3 (['abcC3']) + c: type ]]..'\022'..[[5; value: abcC3 (['abcC3']), expr: abcC3 (['abcC3']) == =abcC3= {{{2 setreg('d', ['abcD3']) - d: type V; value: abcD3]].."\x00 (['abcD3']), expr: abcD3\x00"..[[ (['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]].."\x002\x00abc\x003\x00 (['1', '2', 'abc', '3']), expr: 1\x002\x00abc\x003\x00"..[[ (['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 @@ -304,7 +304,7 @@ describe('eval', function() 3 == {{{2 setreg('f', [1, 2, 3]) - f: type V; value: 1]].."\x002\x003\x00 (['1', '2', '3']), expr: 1\x002\x003\x00"..[[ (['1', '2', '3']) + f: type V; value: 1]].."\0002\0003\000 (['1', '2', '3']), expr: 1\0002\0003\000"..[[ (['1', '2', '3']) == 1 2 @@ -312,49 +312,49 @@ describe('eval', function() == {{{1 Appending lists with setreg() {{{2 setreg('A', ['abcA3c'], 'c') - A: type v; value: abcA3]].."\x00abcA3c (['abcA3', 'abcA3c']), expr: abcA3\x00"..[[abcA3c (['abcA3', 'abcA3c']) + 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]].."\x00abcB3l\x00 (['abcB3', 'abcB3l']), expr: abcB3\x00abcB3l\x00"..[[ (['abcB3', 'abcB3l']) + b: type V; value: abcB3]].."\000abcB3l\000 (['abcB3', 'abcB3l']), expr: abcB3\000abcB3l\000"..[[ (['abcB3', 'abcB3l']) == abcB3 abcB3l == {{{2 setreg('C', ['abcC3b'], 'lb') - C: type ]].."\x166; value: abcC3\x00abcC3b (['abcC3', 'abcC3b']), expr: abcC3\x00"..[[abcC3b (['abcC3', 'abcC3b']) + 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]].."\x00abcD32\x00 (['abcD3', 'abcD32']), expr: abcD3\x00abcD32\x00"..[[ (['abcD3', '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]].."\x00abcA3c\x00abcA32\x00 (['abcA3', 'abcA3c', 'abcA32']), expr: abcA3\x00abcA3c\x00abcA32\x00"..[[ (['abcA3', 'abcA3c', '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]].."\x00abcB3l\x00abcB3c (['abcB3', 'abcB3l', 'abcB3c']), expr: abcB3\x00abcB3l\x00"..[[abcB3c (['abcB3', 'abcB3l', 'abcB3c']) + 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]].."\x00abcC3b\x00abcC3l\x00 (['abcC3', 'abcC3b', 'abcC3l']), expr: abcC3\x00abcC3b\x00abcC3l\x00"..[[ (['abcC3', 'abcC3b', 'abcC3l']) + 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 ]].."\x166; value: abcD3\x00abcD32\x00abcD3b (['abcD3', 'abcD32', 'abcD3b']), expr: abcD3\x00abcD32\x00"..[[abcD3b (['abcD3', 'abcD32', 'abcD3b']) + D: type ]].."\0226; value: abcD3\000abcD32\000abcD3b (['abcD3', 'abcD32', 'abcD3b']), expr: abcD3\000abcD32\000"..[[abcD3b (['abcD3', 'abcD32', 'abcD3b']) == =abcD3 = abcD32 @@ -370,50 +370,50 @@ describe('eval', function() expect( '\n'.. '{{{1 Appending lists with NL with setreg()\n'.. - "{{{2 setreg('A', ['\x00', 'abcA3l2'], 'l')\n".. - "A: type V; value: abcA3\x00abcA3c\x00abcA32\x00\x00\x00abcA3l2\x00 (['abcA3', 'abcA3c', 'abcA32', '\x00', 'abcA3l2']), expr: abcA3\x00abcA3c\x00abcA32\x00\x00\x00abcA3l2\x00 (['abcA3', 'abcA3c', 'abcA32', '\x00', 'abcA3l2'])\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'.. - '\x00\n'.. + '\000\n'.. 'abcA3l2\n'.. '==') execute('%delete') execute([=[call SetReg('B', ["\n", 'abcB3c2'], 'c')]=]) expect( '\n'.. - "{{{2 setreg('B', ['\x00', 'abcB3c2'], 'c')\n".. - "B: type v; value: abcB3\x00abcB3l\x00abcB3c\x00\x00\x00abcB3c2 (['abcB3', 'abcB3l', 'abcB3c', '\x00', 'abcB3c2']), expr: abcB3\x00abcB3l\x00abcB3c\x00\x00\x00abcB3c2 (['abcB3', 'abcB3l', 'abcB3c', '\x00', 'abcB3c2'])\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'.. - '\x00\n'.. + '\000\n'.. 'abcB3c2=') execute('%delete') execute([=[call SetReg('C', ["\n", 'abcC3b2'], 'b')]=]) expect( '\n'.. - "{{{2 setreg('C', ['\x00', 'abcC3b2'], 'b')\n".. - "C: type \x167; value: abcC3\x00abcC3b\x00abcC3l\x00\x00\x00abcC3b2 (['abcC3', 'abcC3b', 'abcC3l', '\x00', 'abcC3b2']), expr: abcC3\x00abcC3b\x00abcC3l\x00\x00\x00abcC3b2 (['abcC3', 'abcC3b', 'abcC3l', '\x00', 'abcC3b2'])\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'.. - ' \x00\n'.. + ' \000\n'.. ' abcC3b2') execute('%delete') execute([=[call SetReg('D', ["\n", 'abcD3b50'],'b50')]=]) expect( '\n'.. - "{{{2 setreg('D', ['\x00', 'abcD3b50'], 'b50')\n".. - "D: type \x1650; value: abcD3\x00abcD32\x00abcD3b\x00\x00\x00abcD3b50 (['abcD3', 'abcD32', 'abcD3b', '\x00', 'abcD3b50']), expr: abcD3\x00abcD32\x00abcD3b\x00\x00\x00abcD3b50 (['abcD3', 'abcD32', 'abcD3b', '\x00', 'abcD3b50'])\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'.. - ' \x00\n'.. + ' \000\n'.. ' abcD3b50') end) @@ -425,14 +425,14 @@ describe('eval', function() execute([=[call SetReg('a', ['abcA4-0', "\n", "abcA4-2\n", "\nabcA4-3", "abcA4-4\nabcA4-4-2"])]=]) expect( '\n'.. - "{{{2 setreg('a', ['abcA4-0', '\x00', 'abcA4-2\x00', '\x00abcA4-3', 'abcA4-4\x00abcA4-4-2'])\n".. - "a: type V; value: abcA4-0\x00\x00\x00abcA4-2\x00\x00\x00abcA4-3\x00abcA4-4\x00abcA4-4-2\x00 (['abcA4-0', '\x00', 'abcA4-2\x00', '\x00abcA4-3', 'abcA4-4\x00abcA4-4-2']), expr: abcA4-0\x00\x00\x00abcA4-2\x00\x00\x00abcA4-3\x00abcA4-4\x00abcA4-4-2\x00 (['abcA4-0', '\x00', 'abcA4-2\x00', '\x00abcA4-3', 'abcA4-4\x00abcA4-4-2'])\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'.. - '\x00\n'.. - 'abcA4-2\x00\n'.. - '\x00abcA4-3\n'.. - 'abcA4-4\x00abcA4-4-2\n'.. + '\000\n'.. + 'abcA4-2\000\n'.. + '\000abcA4-3\n'.. + 'abcA4-4\000abcA4-4-2\n'.. '==') end) @@ -441,14 +441,14 @@ describe('eval', function() 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', '\x00', 'abcB4c-2\x00', '\x00abcB4c-3', 'abcB4c-4\x00abcB4c-4-2'], 'c')\n".. - "b: type v; value: abcB4c-0\x00\x00\x00abcB4c-2\x00\x00\x00abcB4c-3\x00abcB4c-4\x00abcB4c-4-2 (['abcB4c-0', '\x00', 'abcB4c-2\x00', '\x00abcB4c-3', 'abcB4c-4\x00abcB4c-4-2']), expr: abcB4c-0\x00\x00\x00abcB4c-2\x00\x00\x00abcB4c-3\x00abcB4c-4\x00abcB4c-4-2 (['abcB4c-0', '\x00', 'abcB4c-2\x00', '\x00abcB4c-3', 'abcB4c-4\x00abcB4c-4-2'])\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'.. - '\x00\n'.. - 'abcB4c-2\x00\n'.. - '\x00abcB4c-3\n'.. - 'abcB4c-4\x00abcB4c-4-2=') + '\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() @@ -456,14 +456,14 @@ describe('eval', function() 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', '\x00', 'abcC4l-2\x00', '\x00abcC4l-3', 'abcC4l-4\x00abcC4l-4-2'], 'l')\n".. - "c: type V; value: abcC4l-0\x00\x00\x00abcC4l-2\x00\x00\x00abcC4l-3\x00abcC4l-4\x00abcC4l-4-2\x00 (['abcC4l-0', '\x00', 'abcC4l-2\x00', '\x00abcC4l-3', 'abcC4l-4\x00abcC4l-4-2']), expr: abcC4l-0\x00\x00\x00abcC4l-2\x00\x00\x00abcC4l-3\x00abcC4l-4\x00abcC4l-4-2\x00 (['abcC4l-0', '\x00', 'abcC4l-2\x00', '\x00abcC4l-3', 'abcC4l-4\x00abcC4l-4-2'])\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'.. - '\x00\n'.. - 'abcC4l-2\x00\n'.. - '\x00abcC4l-3\n'.. - 'abcC4l-4\x00abcC4l-4-2\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() @@ -471,28 +471,50 @@ describe('eval', function() 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', '\x00', 'abcD4b-2\x00', '\x00abcD4b-3', 'abcD4b-4\x00abcD4b-4-2'], 'b')\n".. - "d: type \x1619; value: abcD4b-0\x00\x00\x00abcD4b-2\x00\x00\x00abcD4b-3\x00abcD4b-4\x00abcD4b-4-2 (['abcD4b-0', '\x00', 'abcD4b-2\x00', '\x00abcD4b-3', 'abcD4b-4\x00abcD4b-4-2']), expr: abcD4b-0\x00\x00\x00abcD4b-2\x00\x00\x00abcD4b-3\x00abcD4b-4\x00abcD4b-4-2 (['abcD4b-0', '\x00', 'abcD4b-2\x00', '\x00abcD4b-3', 'abcD4b-4\x00abcD4b-4-2'])\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'.. - ' \x00\n'.. - ' abcD4b-2\x00\n'.. - ' \x00abcD4b-3\n'.. - ' abcD4b-4\x00abcD4b-4-2') + ' \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', '\x00', 'abcE4b10-2\x00', '\x00abcE4b10-3', 'abcE4b10-4\x00abcE4b10-4-2'], 'b10')\n".. - "e: type \x1610; value: abcE4b10-0\x00\x00\x00abcE4b10-2\x00\x00\x00abcE4b10-3\x00abcE4b10-4\x00abcE4b10-4-2 (['abcE4b10-0', '\x00', 'abcE4b10-2\x00', '\x00abcE4b10-3', 'abcE4b10-4\x00abcE4b10-4-2']), expr: abcE4b10-0\x00\x00\x00abcE4b10-2\x00\x00\x00abcE4b10-3\x00abcE4b10-4\x00abcE4b10-4-2 (['abcE4b10-0', '\x00', 'abcE4b10-2\x00', '\x00abcE4b10-3', 'abcE4b10-4\x00abcE4b10-4-2'])\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'.. - ' \x00\n'.. - ' abcE4b10-2\x00\n'.. - ' \x00abcE4b10-3\n'.. - ' abcE4b10-4\x00abcE4b10-4-2') + ' \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() @@ -507,14 +529,14 @@ describe('eval', function() /: type v; value: abc/ (['abc/']), expr: abc/ (['abc/']) == =abc/= - {{{2 setreg('/', ['abc/]]..'\x00'..[[']) - /: type v; value: abc/]].."\x00 (['abc/\x00']), expr: abc/\x00 (['abc/\x00"..[[']) + {{{2 setreg('/', ['abc/]]..'\000'..[[']) + /: type v; value: abc/]].."\000 (['abc/\000']), expr: abc/\000 (['abc/\000"..[[']) == - =abc/]]..'\x00'..[[= + =abc/]]..'\000'..[[= {{{2 setreg('=', ['"abc/"']) =: type v; value: abc/ (['abc/']), expr: "abc/" (['"abc/"']) - {{{2 setreg('=', ['"abc/]]..'\x00'..[["']) - =: type v; value: abc/]].."\x00 (['abc/\x00"..[[']), expr: "abc/]]..'\x00'..[[" (['"abc/]]..'\x00'..[["'])]]) + {{{2 setreg('=', ['"abc/]]..'\000'..[["']) + =: type v; value: abc/]].."\000 (['abc/\000"..[[']), expr: "abc/]]..'\000'..[[" (['"abc/]]..'\000'..[["'])]]) end) if has_clipboard() then @@ -693,4 +715,22 @@ describe('eval', function() 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/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/increment_spec.lua b/test/functional/legacy/increment_spec.lua index 6139ec0b67..4aa24c0d53 100644 --- a/test/functional/legacy/increment_spec.lua +++ b/test/functional/legacy/increment_spec.lua @@ -708,6 +708,25 @@ describe('Ctrl-A/Ctrl-X on visual selections', function() 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) @@ -720,4 +739,10 @@ describe('Ctrl-A/Ctrl-X on visual selections', function() 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/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/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/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/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/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 index cc76794267..5b6ea88c34 100644 --- a/test/functional/plugin/helpers.lua +++ b/test/functional/plugin/helpers.lua @@ -25,7 +25,7 @@ local session = nil local reset = function(...) if session then - session:exit(0) + session:close() end session = spawn(nvim_argv(...)) set_session(session) 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 index 18ff0f5156..60ba88e55b 100644 --- a/test/functional/plugin/msgpack_spec.lua +++ b/test/functional/plugin/msgpack_spec.lua @@ -1,6 +1,9 @@ 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 @@ -21,10 +24,8 @@ describe('In autoload/msgpack.vim', function() end local nan = -(1.0/0.0-1.0/0.0) - local minus_nan = 1.0/0.0-1.0/0.0 local inf = 1.0/0.0 local minus_inf = -(1.0/0.0) - local has_minus_nan = tostring(nan) ~= tostring(minus_nan) describe('function msgpack#equal', function() local msgpack_eq = function(expected, a, b) @@ -160,9 +161,9 @@ describe('In autoload/msgpack.vim', function() 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)') - if has_minus_nan then - msgpack_eq(0, '(1.0/0.0-1.0/0.0)', '-(1.0/0.0-1.0/0.0)') - end + -- 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)') @@ -178,10 +179,8 @@ describe('In autoload/msgpack.vim', function() 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)') - if has_minus_nan then - msgpack_eq(0, sp('float', '(1.0/0.0-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-1.0/0.0)') - end + 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)') @@ -207,10 +206,8 @@ describe('In autoload/msgpack.vim', function() 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)')) - if has_minus_nan then - msgpack_eq(0, sp('float', '(1.0/0.0-1.0/0.0)'), - sp('float', '-(1.0/0.0-1.0/0.0)')) - end + 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')) @@ -392,9 +389,7 @@ describe('In autoload/msgpack.vim', function() 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)')) - if has_minus_nan then - string_eq('-nan', sp('float', '(1.0/0.0-1.0/0.0)')) - end + 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')) @@ -413,11 +408,15 @@ describe('In autoload/msgpack.vim', function() string_eq('0.0', '0.0') string_eq('inf', '(1.0/0.0)') string_eq('-inf', '-(1.0/0.0)') - if has_minus_nan then - string_eq('-nan', '(1.0/0.0-1.0/0.0)') - end + 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() @@ -532,6 +531,20 @@ describe('In autoload/msgpack.vim', function() 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() @@ -547,8 +560,11 @@ describe('In autoload/msgpack.vim', function() end if expected_val_full == expected_val_full then eq(expected_val_full, nvim_eval('g:__val')) - else - eq(tostring(expected_val_full), tostring(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 @@ -615,7 +631,6 @@ describe('In autoload/msgpack.vim', function() eval_eq('float', inf, 'inf') eval_eq('float', minus_inf, '-inf') eval_eq('float', nan, 'nan') - eval_eq('float', minus_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') diff --git a/test/functional/plugin/shada_spec.lua b/test/functional/plugin/shada_spec.lua index 4100a30452..aad0e366bf 100644 --- a/test/functional/plugin/shada_spec.lua +++ b/test/functional/plugin/shada_spec.lua @@ -4,7 +4,7 @@ local eq, nvim_eval, nvim_command, nvim, exc_exec, funcs, nvim_feed, curbuf = helpers.funcs, helpers.feed, helpers.curbuf local neq = helpers.neq -local msgpack = require('MessagePack') +local mpack = require('mpack') local plugin_helpers = require('test.functional.plugin.helpers') local reset = plugin_helpers.reset @@ -15,13 +15,13 @@ local get_shada_rw = shada_helpers.get_shada_rw local mpack_eq = function(expected, mpack_result) local mpack_keys = {'type', 'timestamp', 'length', 'value'} - local unpacker = msgpack.unpacker(mpack_result) + local unpack = mpack.Unpacker() local actual = {} - 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 <= #mpack_result do + val, off = unpack(mpack_result, off) if i % 4 == 0 then cur = {} actual[#actual + 1] = cur @@ -78,36 +78,6 @@ describe('In autoload/shada.vim', function() return ('{"_TYPE": v:msgpack_types.%s, "_VAL": %s}'):format(typ, val) end - local st_meta = { - __pairs=function(table) - local ret = {} - local next_key = nil - local num_keys = 0 - while true do - next_key = next(table, next_key) - if next_key == nil then - break - end - num_keys = num_keys + 1 - ret[num_keys] = {next_key, table[next_key]} - end - table.sort(ret, function(a, b) - return a[1] < b[1] - end) - local state = {i=0} - return (function(state_, _) - state_.i = state_.i + 1 - if ret[state_.i] then - return table.unpack(ret[state_.i]) - end - end), state - end - } - - local st = function(table) - return setmetatable(table, st_meta) - end - describe('function shada#mpack_to_sd', function() local mpack2sd = function(arg) return ('shada#mpack_to_sd(%s)'):format(arg) @@ -184,7 +154,7 @@ describe('In autoload/shada.vim', function() ' + b 2', ' + c column 3', ' + d 4', - }, {{type=1, timestamp=0, data=st({a=1, b=2, c=3, d=4})}}) + }, {{type=1, timestamp=0, data={a=1, b=2, c=3, d=4}}}) sd2strings_eq({ 'Header with timestamp ' .. epoch .. ':', ' % Key Value', @@ -2215,7 +2185,7 @@ describe('In plugin/shada.vim', function() describe('event BufWriteCmd', function() it('works', function() nvim('set_var', 'shada#add_own_header', 0) - curbuf('set_line_slice', 0, 0, true, true, { + curbuf('set_lines', 0, 1, true, { 'Jump with timestamp ' .. epoch .. ':', ' % Key________ Description Value', ' + n name \'A\'', @@ -2271,7 +2241,7 @@ describe('In plugin/shada.vim', function() describe('event FileWriteCmd', function() it('works', function() nvim('set_var', 'shada#add_own_header', 0) - curbuf('set_line_slice', 0, 0, true, true, { + curbuf('set_lines', 0, 1, true, { 'Jump with timestamp ' .. epoch .. ':', ' % Key________ Description Value', ' + n name \'A\'', @@ -2310,7 +2280,7 @@ describe('In plugin/shada.vim', function() describe('event FileAppendCmd', function() it('works', function() nvim('set_var', 'shada#add_own_header', 0) - curbuf('set_line_slice', 0, 0, true, true, { + curbuf('set_lines', 0, 1, true, { 'Jump with timestamp ' .. epoch .. ':', ' % Key________ Description Value', ' + n name \'A\'', @@ -2512,7 +2482,7 @@ describe('syntax/shada.vim', function() it('works', function() nvim_command('syntax on') nvim_command('setlocal syntax=shada') - curbuf('set_line_slice', 0, 0, true, true, { + curbuf('set_lines', 0, 1, true, { 'Header with timestamp ' .. epoch .. ':', ' % Key Value', ' + t "test"', @@ -2848,3 +2818,4 @@ describe('syntax/shada.vim', function() 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 6e8a3b89cd..c30ad6d8c2 100644 --- a/test/functional/provider/define_spec.lua +++ b/test/functional/provider/define_spec.lua @@ -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, arguments) - eq('test-handler', method) - eq({'arg1', 'arg2', 'arg3'}, arguments[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, arguments) - eq('test-handler', method) - eq({1, 1}, arguments[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, arguments) - eq('test-handler', method) - eq({'arg'}, arguments[1]) - eq({1, 1}, arguments[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, arguments) - eq('test-handler', method) - eq({'arg'}, arguments[1]) - eq(5, arguments[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, arguments) - eq('test-handler', method) - eq({'arg'}, arguments[1]) - eq(5, arguments[2]) - eq(1, arguments[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, arguments) - eq('test-handler', method) - eq({'arg'}, arguments[1]) - eq(5, arguments[2]) - eq(1, arguments[3]) - eq('b', arguments[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, 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 + 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) - 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, arguments) - eq('test-handler', method) - eq('x.c', arguments[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, arguments) - eq('test-handler', method) - eq({{1, 'a', {'b', 'c'}}}, arguments) - 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, arguments) - eq('test-handler', method) - eq({{1, 'a', {'b', 'c'}}, 4}, arguments) - 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/shada/compatibility_spec.lua b/test/functional/shada/compatibility_spec.lua index 2ca0b16e75..1fa88c58e5 100644 --- a/test/functional/shada/compatibility_spec.lua +++ b/test/functional/shada/compatibility_spec.lua @@ -270,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 diff --git a/test/functional/shada/helpers.lua b/test/functional/shada/helpers.lua index 146ae8d51e..d4eb7f57bd 100644 --- a/test/functional/shada/helpers.lua +++ b/test/functional/shada/helpers.lua @@ -3,7 +3,7 @@ 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 = '' @@ -20,14 +20,8 @@ local function nvim_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 @@ -66,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 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 822ab3913c..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) diff --git a/test/functional/shada/variables_spec.lua b/test/functional/shada/variables_spec.lua index 3becf1bc32..7ceeafdc71 100644 --- a/test/functional/shada/variables_spec.lua +++ b/test/functional/shada/variables_spec.lua @@ -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') diff --git a/test/functional/terminal/cursor_spec.lua b/test/functional/terminal/cursor_spec.lua index e9cb010003..c15da2f760 100644 --- a/test/functional/terminal/cursor_spec.lua +++ b/test/functional/terminal/cursor_spec.lua @@ -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/tui_spec.lua b/test/functional/terminal/tui_spec.lua index 838d05a6df..364ca327a4 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -41,7 +41,7 @@ describe('tui', function() -- INSERT -- | -- TERMINAL -- | ]]) - feed('\x1b') + feed('\027') screen:expect([[ abc | test1 | @@ -57,7 +57,7 @@ describe('tui', 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 | @@ -87,7 +87,7 @@ describe('tui', function() -- Example: for input ALT+j: -- * Vim (Nvim prior to #3982) sets high-bit, inserts "ê". -- * Nvim (after #3982) inserts "j". - feed('i\x1bj') + feed('i\027j') screen:expect([[ j{1: } | ~ | @@ -101,9 +101,9 @@ describe('tui', function() 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: } | ~ | @@ -116,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: } | ~ | @@ -136,7 +136,7 @@ describe('tui', function() -- INSERT (paste) -- | -- TERMINAL -- | ]]) - feed('\x1b[201~') + feed('\027[201~') screen:expect([[ pasted from terminal{1: } | ~ | @@ -154,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 | @@ -204,7 +202,7 @@ describe('tui focus event handling', function() end) it('can handle focus events in normal mode', function() - feed('\x1b[I') + feed('\027[I') screen:expect([[ {1: } | ~ | @@ -215,7 +213,7 @@ describe('tui focus event handling', function() -- TERMINAL -- | ]]) - feed('\x1b[O') + feed('\027[O') screen:expect([[ {1: } | ~ | @@ -230,7 +228,7 @@ describe('tui focus event handling', function() it('can handle focus events in insert mode', function() execute('set noshowmode') feed('i') - feed('\x1b[I') + feed('\027[I') screen:expect([[ {1: } | ~ | @@ -240,7 +238,7 @@ describe('tui focus event handling', function() gained | -- TERMINAL -- | ]]) - feed('\x1b[O') + feed('\027[O') screen:expect([[ {1: } | ~ | @@ -254,7 +252,7 @@ describe('tui focus event handling', function() it('can handle focus events in cmdline mode', function() feed(':') - feed('\x1b[I') + feed('\027[I') screen:expect([[ | ~ | @@ -264,7 +262,7 @@ describe('tui focus event handling', function() g{1:a}ined | -- TERMINAL -- | ]]) - feed('\x1b[O') + feed('\027[O') screen:expect([[ | ~ | @@ -281,7 +279,7 @@ describe('tui focus event handling', function() execute('set laststatus=0') execute('set noshowmode') execute('terminal') - feed('\x1b[I') + feed('\027[I') screen:expect([[ ready $ | [Process exited 0]{1: } | @@ -291,7 +289,7 @@ describe('tui focus event handling', function() gained | -- TERMINAL -- | ]]) - feed('\x1b[O') + feed('\027[O') screen:expect([[ ready $ | [Process exited 0]{1: } | diff --git a/test/functional/ui/highlight_spec.lua b/test/functional/ui/highlight_spec.lua index 6a89b0983d..06139277b2 100644 --- a/test/functional/ui/highlight_spec.lua +++ b/test/functional/ui/highlight_spec.lua @@ -1,5 +1,6 @@ local helpers = require('test.functional.helpers') local Screen = require('test.functional.ui.screen') +local os = require('os') local clear, feed = helpers.clear, helpers.feed local execute, request, eq = helpers.execute, helpers.request, helpers.eq @@ -16,6 +17,73 @@ describe('color scheme compatibility', function() 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 + + before_each(function() + clear() + screen = Screen.new(20,5) + screen:attach() + --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() + 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) + describe('Default highlight groups', function() -- Test the default attributes for highlight groups shown by the :highlight @@ -38,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 @@ -193,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/mouse_spec.lua b/test/functional/ui/mouse_spec.lua index da9d6a0cd2..d0d791308b 100644 --- a/test/functional/ui/mouse_spec.lua +++ b/test/functional/ui/mouse_spec.lua @@ -426,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 80f46326ee..a11fab18a2 100644 --- a/test/functional/ui/screen.lua +++ b/test/functional/ui/screen.lua @@ -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) 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 4bb9707cda..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 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, 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,32 +121,196 @@ 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() @@ -129,16 +341,424 @@ describe('completion', function() end ) it('completes on each input char', function () - feed('i<C-x><C-u>gu<Down><C-y>') + 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<BS><C-n><C-n><Esc>') + 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) |