diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/config/paths.lua.in | 1 | ||||
-rw-r--r-- | test/functional/ex_cmds/recover_spec.lua | 3 | ||||
-rw-r--r-- | test/functional/ex_cmds/wviminfo_spec.lua | 37 | ||||
-rw-r--r-- | test/functional/ex_getln/history_spec.lua | 36 | ||||
-rw-r--r-- | test/functional/helpers.lua | 77 | ||||
-rw-r--r-- | test/functional/legacy/074_global_var_in_viminfo_spec.lua | 24 | ||||
-rw-r--r-- | test/functional/shada/buffers_spec.lua | 51 | ||||
-rw-r--r-- | test/functional/shada/compatibility_spec.lua | 453 | ||||
-rw-r--r-- | test/functional/shada/errors_spec.lua | 490 | ||||
-rw-r--r-- | test/functional/shada/helpers.lua | 94 | ||||
-rw-r--r-- | test/functional/shada/history_spec.lua | 312 | ||||
-rw-r--r-- | test/functional/shada/marks_spec.lua | 180 | ||||
-rw-r--r-- | test/functional/shada/merging_spec.lua | 1032 | ||||
-rw-r--r-- | test/functional/shada/registers_spec.lua | 163 | ||||
-rw-r--r-- | test/functional/shada/shada_spec.lua | 232 | ||||
-rw-r--r-- | test/functional/shada/variables_spec.lua | 139 |
16 files changed, 3289 insertions, 35 deletions
diff --git a/test/config/paths.lua.in b/test/config/paths.lua.in index a13230ed28..80cc5629d1 100644 --- a/test/config/paths.lua.in +++ b/test/config/paths.lua.in @@ -7,6 +7,7 @@ end module.test_include_path = "${CMAKE_BINARY_DIR}/test/includes/post" module.test_libnvim_path = "${TEST_LIBNVIM_PATH}" +module.test_source_path = "${CMAKE_SOURCE_DIR}" table.insert(module.include_paths, "${CMAKE_BINARY_DIR}/include") return module diff --git a/test/functional/ex_cmds/recover_spec.lua b/test/functional/ex_cmds/recover_spec.lua index b92739e40e..a24a60af81 100644 --- a/test/functional/ex_cmds/recover_spec.lua +++ b/test/functional/ex_cmds/recover_spec.lua @@ -47,7 +47,8 @@ describe(':preserve', function() --TODO(justinmk): this is an ugly hack to force `helpers` to support --multiple sessions. - local nvim2 = helpers.spawn({helpers.nvim_prog, '-u', 'NONE', '--embed'}) + local nvim2 = helpers.spawn({helpers.nvim_prog, '-u', 'NONE', '--embed'}, + true) helpers.set_session(nvim2) source(init) diff --git a/test/functional/ex_cmds/wviminfo_spec.lua b/test/functional/ex_cmds/wviminfo_spec.lua index f4911cd3e8..0c85157a0f 100644 --- a/test/functional/ex_cmds/wviminfo_spec.lua +++ b/test/functional/ex_cmds/wviminfo_spec.lua @@ -3,8 +3,8 @@ local clear, execute, eq, neq, spawn, nvim_prog, set_session, wait, write_file = helpers.clear, helpers.execute, helpers.eq, helpers.neq, helpers.spawn, helpers.nvim_prog, helpers.set_session, helpers.wait, helpers.write_file -describe(':wviminfo', function() - local viminfo_file = 'wviminfo_test' +describe(':wshada', function() + local shada_file = 'wshada_test' local session before_each(function() @@ -17,38 +17,41 @@ describe(':wviminfo', function() '--cmd', 'set swapfile'}) set_session(session) - os.remove(viminfo_file) + os.remove(shada_file) end) - it('creates a viminfo file', function() + it('creates a shada file', function() -- file should _not_ exist - eq(nil, lfs.attributes(viminfo_file)) - execute('wv! '..viminfo_file) + eq(nil, lfs.attributes(shada_file)) + execute('wsh! '..shada_file) wait() -- file _should_ exist - neq(nil, lfs.attributes(viminfo_file)) + neq(nil, lfs.attributes(shada_file)) end) it('overwrites existing files', function() - local text = 'wviminfo test' + local text = 'wshada test' -- Create a dummy file - write_file(viminfo_file, text) + write_file(shada_file, text) -- sanity check - eq(text, io.open(viminfo_file):read()) - neq(nil, lfs.attributes(viminfo_file)) + eq(text, io.open(shada_file):read()) + neq(nil, lfs.attributes(shada_file)) - execute('wv! '..viminfo_file) + execute('wsh! '..shada_file) wait() - -- File should have been overwritten with a viminfo file. - local line1 = io.lines(viminfo_file)() - assert(nil ~= string.find(line1, 'This viminfo file was generated by Nvim'), - viminfo_file..' should be a viminfo-formatted file') + -- File should have been overwritten with a shada file. + local fp = io.open(shada_file, 'r') + local char1 = fp:read(1) + fp:close() + -- ShaDa file starts with a “header” entry + assert(char1:byte() == 0x01, + shada_file..' should be a shada file') end) teardown(function() - os.remove(viminfo_file) + os.remove(shada_file) end) end) diff --git a/test/functional/ex_getln/history_spec.lua b/test/functional/ex_getln/history_spec.lua new file mode 100644 index 0000000000..532c81dab9 --- /dev/null +++ b/test/functional/ex_getln/history_spec.lua @@ -0,0 +1,36 @@ +local helpers = require('test.functional.helpers') +local clear, meths, funcs, eq = + helpers.clear, helpers.meths, helpers.funcs, helpers.eq + +describe('history support code', function() + before_each(clear) + + it('correctly clears start of the history', function() + -- Regression test: check absense of the memory leak when clearing start of + -- the history using ex_getln.c/clr_history(). + eq(1, funcs.histadd(':', 'foo')) + eq(1, funcs.histdel(':')) + eq('', funcs.histget(':', -1)) + end) + + it('correctly clears end of the history', function() + -- Regression test: check absense of the memory leak when clearing end of + -- the history using ex_getln.c/clr_history(). + meths.set_option('history', 1) + eq(1, funcs.histadd(':', 'foo')) + eq(1, funcs.histdel(':')) + eq('', funcs.histget(':', -1)) + end) + + it('correctly removes item from history', function() + -- Regression test: check that ex_getln.c/del_history_idx() correctly clears + -- history index after removing history entry. If it does not then deleting + -- history will result in a double free. + eq(1, funcs.histadd(':', 'foo')) + eq(1, funcs.histadd(':', 'bar')) + eq(1, funcs.histadd(':', 'baz')) + eq(1, funcs.histdel(':', -2)) + eq(1, funcs.histdel(':')) + eq('', funcs.histget(':', -1)) + end) +end) diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index 80cb1e5ce3..a62f7a3d00 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -44,11 +44,15 @@ elseif os.getenv('GDB') then end if prepend_argv then + local new_nvim_argv = {} local len = #prepend_argv + for i = 1, len do + new_nvim_argv[i] = prepend_argv[i] + end for i = 1, #nvim_argv do - prepend_argv[i + len] = nvim_argv[i] + new_nvim_argv[i + len] = nvim_argv[i] end - nvim_argv = prepend_argv + nvim_argv = new_nvim_argv end local session, loop_running, loop_stopped, last_error @@ -174,12 +178,27 @@ local function rawfeed(...) end end -local function spawn(argv) +local function merge_args(...) + local i = 1 + local argv = {} + for anum = 1,select('#', ...) do + local args = select(anum, ...) + if args then + for _, arg in ipairs(args) do + argv[i] = arg + i = i + 1 + end + end + end + return argv +end + +local function spawn(argv, merge) local loop = Loop.new() local msgpack_stream = MsgpackStream.new(loop) local async_session = AsyncSession.new(msgpack_stream) local session = Session.new(async_session) - loop:spawn(argv) + loop:spawn(merge and merge_args(prepend_argv, argv) or argv) return session end @@ -216,9 +235,12 @@ local function execute(...) end -- Dedent the given text and write it to the file name. -local function write_file(name, text) +local function write_file(name, text, dont_dedent) local file = io.open(name, 'w') - file:write(dedent(text)) + if not dont_dedent then + text = dedent(text) + end + file:write(text) file:flush() file:close() end @@ -337,7 +359,40 @@ local exc_exec = function(cmd) return ret end +local function redir_exec(cmd) + nvim_command(([[ + redir => g:__output + silent! execute "%s" + redir END + ]]):format(cmd:gsub('\n', '\\n'):gsub('[\\"]', '\\%0'))) + local ret = nvim_eval('get(g:, "__output", 0)') + nvim_command('unlet! g:__output') + return ret +end + +local function create_callindex(func) + local tbl = {} + setmetatable(tbl, { + __index = function(tbl, arg1) + ret = function(...) return func(arg1, ...) end + tbl[arg1] = ret + return ret + end, + }) + return tbl +end + +local funcs = create_callindex(nvim_call) +local meths = create_callindex(nvim) +local bufmeths = create_callindex(buffer) +local winmeths = create_callindex(window) +local tabmeths = create_callindex(tabpage) +local curbufmeths = create_callindex(curbuf) +local curwinmeths = create_callindex(curwin) +local curtabmeths = create_callindex(curtab) + return { + prepend_argv = prepend_argv, clear = clear, spawn = spawn, dedent = dedent, @@ -374,4 +429,14 @@ return { rmdir = rmdir, mkdir = lfs.mkdir, exc_exec = exc_exec, + redir_exec = redir_exec, + merge_args = merge_args, + funcs = funcs, + meths = meths, + bufmeths = bufmeths, + winmeths = winmeths, + tabmeths = tabmeths, + curbufmeths = curbufmeths, + curwinmeths = curwinmeths, + curtabmeths = curtabmeths, } diff --git a/test/functional/legacy/074_global_var_in_viminfo_spec.lua b/test/functional/legacy/074_global_var_in_viminfo_spec.lua index a89a4181cd..2428b7f74d 100644 --- a/test/functional/legacy/074_global_var_in_viminfo_spec.lua +++ b/test/functional/legacy/074_global_var_in_viminfo_spec.lua @@ -1,14 +1,15 @@ --- Tests for storing global variables in the .viminfo file +-- Tests for storing global variables in the .shada file local helpers, lfs = require('test.functional.helpers'), require('lfs') local clear, execute, eq, neq, eval, wait, spawn = helpers.clear, helpers.execute, helpers.eq, helpers.neq, helpers.eval, helpers.wait, helpers.spawn -describe('storing global variables in viminfo files', function() +describe('storing global variables in ShaDa files', function() + local tempname = 'Xtest-functional-legacy-074' setup(function() clear() - os.remove("Xviminfo") + os.remove(tempname) end) it('is working', function() @@ -27,33 +28,34 @@ describe('storing global variables in viminfo files', function() execute( -- This will cause a few errors, do it silently. 'set visualbell', - 'set viminfo+=!', + 'set shada+=!', "let MY_GLOBAL_DICT={'foo': 1, 'bar': 0, 'longvarible': 1000}", - -- Store a really long list, so line wrapping will occur in viminfo - -- file. + -- Store a really long list. Initially this was testing line wrapping in + -- viminfo, but shada files has no line wrapping, no matter how long the + -- list is. 'let MY_GLOBAL_LIST=range(1,100)' ) eq(test_dict, eval('MY_GLOBAL_DICT')) eq(test_list, eval('MY_GLOBAL_LIST')) - execute('wv! Xviminfo') + execute('wsh! ' .. tempname) wait() - -- Assert that the viminfo file exists. - neq(nil, lfs.attributes('Xviminfo')) + -- Assert that the shada file exists. + neq(nil, lfs.attributes(tempname)) execute('unlet MY_GLOBAL_DICT', 'unlet MY_GLOBAL_LIST') -- Assert that the variables where deleted. eq(0, eval('exists("MY_GLOBAL_DICT")')) eq(0, eval('exists("MY_GLOBAL_LIST")')) - execute('rv! Xviminfo') + execute('rsh! ' .. tempname) eq(test_list, eval('MY_GLOBAL_LIST')) eq(test_dict, eval('MY_GLOBAL_DICT')) end) teardown(function() - os.remove('Xviminfo') + os.remove(tempname) end) end) diff --git a/test/functional/shada/buffers_spec.lua b/test/functional/shada/buffers_spec.lua new file mode 100644 index 0000000000..3666b718f0 --- /dev/null +++ b/test/functional/shada/buffers_spec.lua @@ -0,0 +1,51 @@ +-- ShaDa buffer list saving/reading support +local helpers = require('test.functional.helpers') +local nvim_command, funcs, eq = + helpers.command, helpers.funcs, helpers.eq + +local shada_helpers = require('test.functional.shada.helpers') +local reset, set_additional_cmd, clear = + shada_helpers.reset, shada_helpers.set_additional_cmd, + shada_helpers.clear + +describe('ShaDa support code', function() + testfilename = 'Xtestfile-functional-shada-buffers' + testfilename_2 = 'Xtestfile-functional-shada-buffers-2' + before_each(reset) + after_each(clear) + + it('is able to dump and restore buffer list', function() + set_additional_cmd('set shada+=%') + reset() + nvim_command('edit ' .. testfilename) + nvim_command('edit ' .. testfilename_2) + nvim_command('qall') + reset() + eq(3, funcs.bufnr('$')) + eq('', funcs.bufname(1)) + eq(testfilename, funcs.bufname(2)) + eq(testfilename_2, funcs.bufname(3)) + end) + + it('does not restore buffer list without % in &shada', function() + set_additional_cmd('set shada+=%') + reset() + nvim_command('edit ' .. testfilename) + nvim_command('edit ' .. testfilename_2) + set_additional_cmd('') + nvim_command('qall') + reset() + eq(1, funcs.bufnr('$')) + eq('', funcs.bufname(1)) + end) + + it('does not dump buffer list without % in &shada', function() + nvim_command('edit ' .. testfilename) + nvim_command('edit ' .. testfilename_2) + set_additional_cmd('set shada+=%') + nvim_command('qall') + reset() + eq(1, funcs.bufnr('$')) + eq('', funcs.bufname(1)) + end) +end) diff --git a/test/functional/shada/compatibility_spec.lua b/test/functional/shada/compatibility_spec.lua new file mode 100644 index 0000000000..342dee377b --- /dev/null +++ b/test/functional/shada/compatibility_spec.lua @@ -0,0 +1,453 @@ +-- ShaDa compatibility support +local helpers = require('test.functional.helpers') +local nvim_command, funcs, eq = helpers.command, helpers.funcs, helpers.eq +local exc_exec = helpers.exc_exec + +local shada_helpers = require('test.functional.shada.helpers') +local reset, set_additional_cmd, clear, get_shada_rw = + shada_helpers.reset, shada_helpers.set_additional_cmd, + shada_helpers.clear, shada_helpers.get_shada_rw +local read_shada_file = shada_helpers.read_shada_file + +local wshada, sdrcmd, shada_fname = get_shada_rw('Xtest-functional-shada-compatibility.shada') + +describe('ShaDa forward compatibility support code', function() + before_each(reset) + after_each(function() + clear() + os.remove(shada_fname) + end) + + it('works with search pattern item with BOOL unknown (sX) key value', function() + wshada('\002\001\011\130\162sX\194\162sp\196\001-') + eq(0, exc_exec(sdrcmd())) + os.remove(shada_fname) + nvim_command('wshada ' .. shada_fname) + local found = false + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 2 and not v.value.ss then + eq(false, v.value.sX) + found = true + end + end + eq(true, found) + eq(0, exc_exec(sdrcmd())) + os.remove(shada_fname) + nvim_command('silent! /---/') + nvim_command('wshada ' .. shada_fname) + found = false + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 2 and not v.value.ss then + eq(nil, v.value.sX) + found = true + end + end + eq(true, found) + funcs.garbagecollect(1) + funcs.garbagecollect(1) + nvim_command('rshada! ' .. shada_fname) + funcs.garbagecollect(1) + funcs.garbagecollect(1) + end) + + it('works with s/search pattern item with BOOL unknown (sX) key value', function() + wshada('\002\001\015\131\162sX\194\162ss\195\162sp\196\001-') + eq(0, exc_exec(sdrcmd())) + os.remove(shada_fname) + nvim_command('wshada ' .. shada_fname) + local found = false + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 2 and v.value.ss then + eq(false, v.value.sX) + found = true + end + end + eq(true, found) + eq(0, exc_exec(sdrcmd())) + os.remove(shada_fname) + nvim_command('silent! s/--/---/ge') + nvim_command('wshada ' .. shada_fname) + found = false + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 2 and v.value.ss then + eq(nil, v.value.sX) + found = true + end + end + eq(true, found) + funcs.garbagecollect(1) + funcs.garbagecollect(1) + nvim_command('rshada!' .. shada_fname) + funcs.garbagecollect(1) + funcs.garbagecollect(1) + end) + + it('works with replacement item with BOOL additional value in list', function() + wshada('\003\000\005\146\196\001-\194') + eq(0, exc_exec(sdrcmd())) + os.remove(shada_fname) + nvim_command('wshada ' .. shada_fname) + local found = false + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 3 then + eq(2, #v.value) + eq(false, v.value[2]) + found = true + end + end + eq(true, found) + eq(0, exc_exec(sdrcmd())) + os.remove(shada_fname) + nvim_command('silent! s/--/---/ge') + nvim_command('wshada ' .. shada_fname) + found = false + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 3 then + eq(1, #v.value) + found = true + end + end + eq(true, found) + funcs.garbagecollect(1) + funcs.garbagecollect(1) + nvim_command('rshada!' .. shada_fname) + funcs.garbagecollect(1) + funcs.garbagecollect(1) + end) + + for _, v in ipairs({{name='global mark', mpack='\007\001\018\131\162mX\195\161f\196\006/a/b/c\161nA'}, + {name='jump', mpack='\008\001\018\131\162mX\195\161f\196\006/a/b/c\161l\002'}, + {name='local mark', mpack='\010\001\018\131\162mX\195\161f\196\006/a/b/c\161na'}, + {name='change', mpack='\011\001\015\130\162mX\195\161f\196\006/a/b/c'}, + }) do + it('works with ' .. v.name .. ' item with BOOL unknown (mX) key value', function() + nvim_command('silent noautocmd edit /a/b/c') + eq('/a/b/c', funcs.bufname('%')) + funcs.setline('.', {'1', '2', '3'}) + wshada(v.mpack) + eq(0, exc_exec(sdrcmd(true))) + os.remove(shada_fname) + nvim_command('wshada ' .. shada_fname) + local found = false + for _, subv in ipairs(read_shada_file(shada_fname)) do + if subv.type == v.mpack:byte() then + if subv.value.mX == true then + found = true + end + end + end + eq(true, found) + eq(0, exc_exec(sdrcmd())) + nvim_command('bwipeout!') + funcs.setpos('\'A', {0, 1, 1, 0}) + os.remove(shada_fname) + nvim_command('wshada ' .. shada_fname) + found = false + for _, subv in ipairs(read_shada_file(shada_fname)) do + if subv.type == v.mpack:byte() then + if subv.value.mX == true then + found = true + end + end + end + eq(false, found) + funcs.garbagecollect(1) + funcs.garbagecollect(1) + nvim_command('rshada!' .. shada_fname) + funcs.garbagecollect(1) + funcs.garbagecollect(1) + end) + + if v.name == 'global mark' or v.name == 'local mark' then + it('works with ' .. v.name .. ' item with <C-a> name', function() + nvim_command('silent noautocmd edit /a/b/c') + eq('/a/b/c', funcs.bufname('%')) + funcs.setline('.', {'1', '2', '3'}) + wshada(v.mpack:gsub('n.$', 'n\001') + .. v.mpack:gsub('n.$', 'n\002') + .. v.mpack:gsub('n.$', 'n\003'):gsub('/a/b/c', '/d/e/f')) + eq(0, exc_exec(sdrcmd(true))) + nvim_command('wshada ' .. shada_fname) + local found = 0 + for i, subv in ipairs(read_shada_file(shada_fname)) do + if i == 1 then + eq(1, subv.type) + end + if subv.type == v.mpack:byte() then + if subv.value.mX == true and subv.value.n <= 3 then + found = found + 1 + end + end + end + eq(3, found) + nvim_command('wshada! ' .. shada_fname) + local found = 0 + for i, subv in ipairs(read_shada_file(shada_fname)) do + if i == 1 then + eq(1, subv.type) + end + if subv.type == v.mpack:byte() then + if subv.value.mX == true and subv.value.n <= 3 then + found = found + 1 + end + end + end + eq(0, found) + funcs.garbagecollect(1) + funcs.garbagecollect(1) + nvim_command('rshada!' .. shada_fname) + funcs.garbagecollect(1) + funcs.garbagecollect(1) + end) + end + end + + it('works with register item with BOOL unknown (rX) key', function() + wshada('\005\001\015\131\161na\162rX\194\162rc\145\196\001-') + eq(0, exc_exec(sdrcmd())) + os.remove(shada_fname) + nvim_command('wshada ' .. shada_fname) + local found = false + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 5 and v.value.rX == false then + found = true + end + end + eq(true, found) + eq(0, exc_exec(sdrcmd())) + os.remove(shada_fname) + nvim_command('let @a = "Test"') + nvim_command('wshada ' .. shada_fname) + found = false + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 5 and v.value.rX == false then + found = true + end + end + eq(false, found) + funcs.garbagecollect(1) + funcs.garbagecollect(1) + nvim_command('rshada!' .. shada_fname) + funcs.garbagecollect(1) + funcs.garbagecollect(1) + end) + + it('works with register item with <C-a> name', function() + wshada('\005\001\015\131\161n\001\162rX\194\162rc\145\196\001-') + eq(0, exc_exec(sdrcmd(true))) + nvim_command('wshada ' .. shada_fname) + local found = 0 + for i, v in ipairs(read_shada_file(shada_fname)) do + if i == 1 then + eq(1, v.type) + end + if v.type == 5 then + if v.value.rX == false and v.value.n == 1 then + found = found + 1 + end + end + end + eq(1, found) + nvim_command('wshada! ' .. shada_fname) + local found = 0 + for i, v in ipairs(read_shada_file(shada_fname)) do + if i == 1 then + eq(1, v.type) + end + if v.type == 5 then + if v.value.rX == false and v.value.n == 1 then + found = found + 1 + end + end + end + eq(0, found) + funcs.garbagecollect(1) + funcs.garbagecollect(1) + nvim_command('rshada!' .. shada_fname) + funcs.garbagecollect(1) + funcs.garbagecollect(1) + end) + + 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.getregtype('a')) + nvim_command('wshada ' .. shada_fname) + local found = 0 + for i, v in ipairs(read_shada_file(shada_fname)) do + if i == 1 then + eq(1, v.type) + end + if v.type == 5 then + if v.value.rX == false and v.value.rt == 10 then + found = found + 1 + end + end + end + eq(1, found) + nvim_command('wshada! ' .. shada_fname) + local found = 0 + for i, v in ipairs(read_shada_file(shada_fname)) do + if i == 1 then + eq(1, v.type) + end + if v.type == 5 then + if v.value.rX == false and v.value.rt == 10 then + found = found + 1 + end + end + end + eq(0, found) + funcs.garbagecollect(1) + funcs.garbagecollect(1) + nvim_command('rshada!' .. shada_fname) + funcs.garbagecollect(1) + funcs.garbagecollect(1) + end) + + it('works with buffer list item with BOOL unknown (bX) key', function() + nvim_command('set shada+=%') + wshada('\009\000\016\145\130\161f\196\006/a/b/c\162bX\195') + eq(0, exc_exec(sdrcmd())) + eq(2, funcs.bufnr('$')) + eq('/a/b/c', funcs.bufname(2)) + os.remove(shada_fname) + nvim_command('wshada ' .. shada_fname) + local found = false + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 9 and #v.value == 1 and v.value[1].bX == true then + found = true + end + end + eq(true, found) + eq(0, exc_exec(sdrcmd())) + os.remove(shada_fname) + nvim_command('buffer 2') + nvim_command('edit!') + nvim_command('wshada ' .. shada_fname) + found = false + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 5 and v.value.rX == false then + found = true + end + end + eq(false, found) + nvim_command('bwipeout!') + funcs.garbagecollect(1) + funcs.garbagecollect(1) + nvim_command('rshada!' .. shada_fname) + funcs.garbagecollect(1) + funcs.garbagecollect(1) + end) + + it('works with history item with BOOL additional value in list', function() + wshada('\004\000\006\147\000\196\001-\194') + eq(0, exc_exec(sdrcmd())) + os.remove(shada_fname) + nvim_command('wshada ' .. shada_fname) + local found = false + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 4 and v.value[1] == 0 and v.value[2] == '-' then + eq(false, v.value[3]) + eq(3, #v.value) + found = true + end + end + eq(true, found) + eq(0, exc_exec(sdrcmd())) + os.remove(shada_fname) + funcs.histadd(':', '--') + funcs.histadd(':', '-') + nvim_command('wshada ' .. shada_fname) + found = false + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 4 and v.value[1] == 0 and v.value[2] == '-' then + eq(2, #v.value) + found = true + end + end + eq(true, found) + funcs.garbagecollect(1) + funcs.garbagecollect(1) + nvim_command('rshada!' .. shada_fname) + funcs.garbagecollect(1) + funcs.garbagecollect(1) + end) + + it('works with history item with type 10', function() + wshada('\004\000\006\147\010\196\001-\194') + eq(0, exc_exec(sdrcmd())) + nvim_command('wshada ' .. shada_fname) + eq(0, exc_exec(sdrcmd())) + local found = 0 + for i, v in ipairs(read_shada_file(shada_fname)) do + if i == 1 then + eq(1, v.type) + end + if v.type == 4 then + if v.value[1] == 10 and #v.value == 3 and v.value[3] == false then + found = found + 1 + end + end + end + eq(1, found) + nvim_command('wshada! ' .. shada_fname) + local found = 0 + for i, v in ipairs(read_shada_file(shada_fname)) do + if i == 1 then + eq(1, v.type) + end + if v.type == 4 then + if v.value[1] == 10 and #v.value == 3 and v.value[3] == false then + found = found + 1 + end + end + end + eq(0, found) + funcs.garbagecollect(1) + funcs.garbagecollect(1) + nvim_command('rshada!' .. shada_fname) + funcs.garbagecollect(1) + funcs.garbagecollect(1) + end) + + it('works with item with 100 type', function() + wshada('\100\000\006\147\010\196\001-\194') + eq(0, exc_exec(sdrcmd())) + nvim_command('wshada ' .. shada_fname) + eq(0, exc_exec(sdrcmd())) + local found = 0 + for i, v in ipairs(read_shada_file(shada_fname)) do + if i == 1 then + eq(1, v.type) + end + if v.type == 100 then + if v.value[1] == 10 and #v.value == 3 and v.value[3] == false then + found = found + 1 + end + end + end + eq(1, found) + nvim_command('wshada! ' .. shada_fname) + local found = 0 + for i, v in ipairs(read_shada_file(shada_fname)) do + if i == 1 then + eq(1, v.type) + end + if v.type == 100 then + if v.value[1] == 10 and #v.value == 3 and v.value[3] == false then + found = found + 1 + end + end + end + eq(0, found) + funcs.garbagecollect(1) + funcs.garbagecollect(1) + nvim_command('rshada!' .. shada_fname) + funcs.garbagecollect(1) + funcs.garbagecollect(1) + end) +end) diff --git a/test/functional/shada/errors_spec.lua b/test/functional/shada/errors_spec.lua new file mode 100644 index 0000000000..16ae77af02 --- /dev/null +++ b/test/functional/shada/errors_spec.lua @@ -0,0 +1,490 @@ +-- ShaDa errors handling support +local helpers = require('test.functional.helpers') +local nvim_command, eq, exc_exec = helpers.command, helpers.eq, helpers.exc_exec + +local shada_helpers = require('test.functional.shada.helpers') +local reset, clear, get_shada_rw = + shada_helpers.reset, shada_helpers.clear, shada_helpers.get_shada_rw + +local wshada, sdrcmd, shada_fname, clean = + get_shada_rw('Xtest-functional-shada-errors.shada') + +describe('ShaDa error handling', function() + before_each(reset) + after_each(function() + clear() + clean() + end) + + -- Note: most of tests have additional items like sX, mX, rX. These are for + -- valgrind tests, to check for memory leaks (i.e. whether error handling code + -- does (not) forget to call ga_clear). Not needed for array-based items like + -- history because they are not using ad_ga. + + it('does not fail on empty file', function() + wshada('') + eq(0, exc_exec(sdrcmd())) + end) + + it('fails on zero', function() + wshada('\000') + eq('Vim(rshada):E576: Error while reading ShaDa file: expected positive integer at position 0, but got nothing', exc_exec(sdrcmd())) + end) + + it('fails on missing item', function() + wshada('\000\000\000') + eq('Vim(rshada):E576: Error while reading ShaDa file: there is an item at position 0 that must not be there: Missing items are for internal uses only', exc_exec(sdrcmd())) + end) + + it('fails on -2 type', function() + wshada('\254\000\000') + eq('Vim(rshada):E576: Error while reading ShaDa file: expected positive integer at position 0', exc_exec(sdrcmd())) + end) + + it('does not fail on header with zero length', function() + -- Header items are skipped when reading. + wshada('\001\000\000') + eq(0, exc_exec(sdrcmd())) + end) + + it('fails on search pattern item with zero length', function() + wshada('\002\000\000') + eq('Vim(rshada):E576: Failed to parse ShaDa file: incomplete msgpack string at position 3', exc_exec(sdrcmd())) + end) + + it('fails on search pattern item with -2 timestamp', function() + wshada('\002\254\000') + eq('Vim(rshada):E576: Error while reading ShaDa file: expected positive integer at position 1', exc_exec(sdrcmd())) + end) + + it('fails on search pattern item with -2 length', function() + wshada('\002\000\254') + eq('Vim(rshada):E576: Error while reading ShaDa file: expected positive integer at position 2', exc_exec(sdrcmd())) + end) + + it('fails on search pattern item with length greater then file length', function() + wshada('\002\000\002\000') + eq('Vim(rshada):E576: Error while reading ShaDa file: last entry specified that it occupies 2 bytes, but file ended earlier', exc_exec(sdrcmd())) + end) + + it('fails on search pattern item with invalid byte', function() + -- 195 (== 0xC1) cannot start any valid messagepack entry (the only byte + -- that cannot do this). Specifically unpack_template.h contains + -- + -- //case 0xc1: // string + -- // again_terminal_trail(NEXT_CS(p), p+1); + -- + -- (literally: commented out code) which means that in place of this code + -- `goto _failed` is used from default: case. I do not know any other way to + -- get MSGPACK_UNPACK_PARSE_ERROR and not MSGPACK_UNPACK_CONTINUE or + -- MSGPACK_UNPACK_EXTRA_BYTES. + wshada('\002\000\001\193') + eq('Vim(rshada):E576: Failed to parse ShaDa file due to a msgpack parser error at position 3', exc_exec(sdrcmd())) + end) + + it('fails on search pattern item with incomplete map', function() + wshada('\002\000\001\129') + eq('Vim(rshada):E576: Failed to parse ShaDa file: incomplete msgpack string at position 3', exc_exec(sdrcmd())) + end) + + it('fails on search pattern item without a pattern', function() + wshada('\002\000\005\129\162sX\192') + eq('Vim(rshada):E575: Error while reading ShaDa file: search pattern entry at position 0 has no pattern', exc_exec(sdrcmd())) + end) + + it('fails on search pattern with extra bytes', function() + wshada('\002\000\002\128\000') + eq('Vim(rshada):E576: Failed to parse ShaDa file: extra bytes in msgpack string at position 3', exc_exec(sdrcmd())) + end) + + it('fails on search pattern item with NIL value', function() + wshada('\002\000\001\192') + eq('Vim(rshada):E575: Error while reading ShaDa file: search pattern entry at position 0 is not a dictionary', exc_exec(sdrcmd())) + end) + + -- sp entry is here because it causes an allocation. + it('fails on search pattern item with BIN key', function() + wshada('\002\000\014\131\162sp\196\001a\162sX\192\196\000\000') + eq('Vim(rshada):E575: Error while reading ShaDa file: search pattern entry at position 0 has key which is not a string', exc_exec(sdrcmd())) + end) + + -- sp entry is here because it causes an allocation. + it('fails on search pattern item with empty key', function() + wshada('\002\000\013\131\162sp\196\001a\162sX\192\160\000') + eq('Vim(rshada):E575: Error while reading ShaDa file: search pattern entry at position 0 has empty key', exc_exec(sdrcmd())) + end) + + it('fails on search pattern item with NIL magic key value', function() + wshada('\002\000\009\130\162sX\192\162sm\192') + eq('Vim(rshada):E575: Error while reading ShaDa file: search pattern entry at position 0 has sm key value which is not a boolean', exc_exec(sdrcmd())) + end) + + it('fails on search pattern item with NIL smartcase key value', function() + wshada('\002\000\009\130\162sX\192\162sc\192') + eq('Vim(rshada):E575: Error while reading ShaDa file: search pattern entry at position 0 has sc key value which is not a boolean', exc_exec(sdrcmd())) + end) + + it('fails on search pattern item with NIL has_line_offset key value', function() + wshada('\002\000\009\130\162sX\192\162sl\192') + eq('Vim(rshada):E575: Error while reading ShaDa file: search pattern entry at position 0 has sl key value which is not a boolean', exc_exec(sdrcmd())) + end) + + it('fails on search pattern item with NIL place_cursor_at_end key value', function() + wshada('\002\000\009\130\162sX\192\162se\192') + eq('Vim(rshada):E575: Error while reading ShaDa file: search pattern entry at position 0 has se key value which is not a boolean', exc_exec(sdrcmd())) + end) + + it('fails on search pattern item with NIL is_last_used key value', function() + wshada('\002\000\009\130\162sX\192\162su\192') + eq('Vim(rshada):E575: Error while reading ShaDa file: search pattern entry at position 0 has su key value which is not a boolean', exc_exec(sdrcmd())) + end) + + it('fails on search pattern item with NIL is_substitute_pattern key value', function() + wshada('\002\000\009\130\162sX\192\162ss\192') + eq('Vim(rshada):E575: Error while reading ShaDa file: search pattern entry at position 0 has ss key value which is not a boolean', exc_exec(sdrcmd())) + end) + + it('fails on search pattern item with NIL highlighted key value', function() + wshada('\002\000\009\130\162sX\192\162sh\192') + eq('Vim(rshada):E575: Error while reading ShaDa file: search pattern entry at position 0 has sh key value which is not a boolean', exc_exec(sdrcmd())) + end) + + it('fails on search pattern item with NIL offset key value', function() + wshada('\002\000\009\130\162sX\192\162so\192') + eq('Vim(rshada):E575: Error while reading ShaDa file: search pattern entry at position 0 has so key value which is not an integer', exc_exec(sdrcmd())) + end) + + it('fails on search pattern item with NIL pat key value', function() + wshada('\002\000\009\130\162sX\192\162sp\192') + eq('Vim(rshada):E575: Error while reading ShaDa file: search pattern entry at position 0 has sp key value which is not a binary', exc_exec(sdrcmd())) + end) + + it('fails on search pattern item with STR pat key value', function() + wshada('\002\000\011\130\162sX\192\162sp\162sp') + eq('Vim(rshada):E575: Error while reading ShaDa file: search pattern entry at position 0 has sp key value which is not a binary', exc_exec(sdrcmd())) + end) + + for _, v in ipairs({{name='global mark', mpack='\007'}, + {name='jump', mpack='\008'}, + {name='local mark', mpack='\010'}, + {name='change', mpack='\011'}, + }) do + local is_mark_test = ({['global mark']=true, ['local mark']=true})[v.name] + + it('fails on ' .. v.name .. ' item with NIL value', function() + wshada(v.mpack .. '\000\001\192') + eq('Vim(rshada):E575: Error while reading ShaDa file: mark entry at position 0 is not a dictionary', exc_exec(sdrcmd())) + end) + + -- f entry is here because it causes an allocation. + it('fails on ' .. v.name .. ' item with BIN key', function() + wshada(v.mpack .. '\000\013\131\161f\196\001/\162mX\192\196\000\000') + eq('Vim(rshada):E575: Error while reading ShaDa file: mark entry at position 0 has key which is not a string', exc_exec(sdrcmd())) + end) + + -- f entry is here because it causes an allocation. + it('fails on ' .. v.name .. ' item with empty key', function() + wshada(v.mpack .. '\000\012\131\161f\196\001/\162mX\192\160\000') + eq('Vim(rshada):E575: Error while reading ShaDa file: mark entry at position 0 has empty key', exc_exec(sdrcmd())) + end) + + it('fails on ' .. v.name .. ' item without f key', function() + wshada(v.mpack .. '\000\008\130\162mX\192\161l\001') + eq('Vim(rshada):E575: Error while reading ShaDa file: mark entry at position 0 is missing file name', exc_exec(sdrcmd())) + end) + + it('fails on ' .. v.name .. ' item with zero l key', function() + wshada(v.mpack .. '\000\013\131\162mX\192\161f\196\001/\161l\000') + eq('Vim(rshada):E575: Error while reading ShaDa file: mark entry at position 0 has invalid line number', exc_exec(sdrcmd())) + end) + + it('fails on ' .. v.name .. ' item with negative l key', function() + wshada(v.mpack .. '\000\013\131\162mX\192\161f\196\001/\161l\255') + eq('Vim(rshada):E575: Error while reading ShaDa file: mark entry at position 0 has invalid line number', exc_exec(sdrcmd())) + end) + + it('fails on ' .. v.name .. ' item with negative c key', function() + wshada(v.mpack .. '\000\013\131\162mX\192\161f\196\001/\161c\255') + eq('Vim(rshada):E575: Error while reading ShaDa file: mark entry at position 0 has invalid column number', exc_exec(sdrcmd())) + end) + + it('fails on ' .. v.name .. ' item with STR n key value', function() + wshada(v.mpack .. '\000\011\130\162mX\192\161n\163spa') + eq(is_mark_test and 'Vim(rshada):E575: Error while reading ShaDa file: mark entry at position 0 has n key value which is not an unsigned integer' or 'Vim(rshada):E575: Error while reading ShaDa file: mark entry at position 0 has n key which is only valid for local and global mark entries', exc_exec(sdrcmd())) + end) + + it('fails on ' .. v.name .. ' item with STR l key value', function() + wshada(v.mpack .. '\000\010\130\162mX\192\161l\162sp') + eq('Vim(rshada):E575: Error while reading ShaDa file: mark entry at position 0 has l key value which is not an integer', exc_exec(sdrcmd())) + end) + + it('fails on ' .. v.name .. ' item with STR c key value', function() + wshada(v.mpack .. '\000\010\130\162mX\192\161c\162sp') + eq('Vim(rshada):E575: Error while reading ShaDa file: mark entry at position 0 has c key value which is not an integer', exc_exec(sdrcmd())) + end) + + it('fails on ' .. v.name .. ' item with STR f key value', function() + wshada(v.mpack .. '\000\010\130\162mX\192\161f\162sp') + eq('Vim(rshada):E575: Error while reading ShaDa file: mark entry at position 0 has f key value which is not a binary', exc_exec(sdrcmd())) + end) + end + + it('fails on register item with NIL value', function() + wshada('\005\000\001\192') + eq('Vim(rshada):E575: Error while reading ShaDa file: register entry at position 0 is not a dictionary', exc_exec(sdrcmd())) + end) + + -- rc entry is here because it causes an allocation + it('fails on register item with BIN key', function() + wshada('\005\000\015\131\162rc\145\196\001a\162rX\192\196\000\000') + eq('Vim(rshada):E575: Error while reading ShaDa file: register entry at position 0 has key which is not a string', exc_exec(sdrcmd())) + end) + + -- rc entry is here because it causes an allocation + it('fails on register item with BIN key', function() + wshada('\005\000\014\131\162rc\145\196\001a\162rX\192\160\000') + eq('Vim(rshada):E575: Error while reading ShaDa file: register entry at position 0 has empty key', exc_exec(sdrcmd())) + end) + + it('fails on register item with NIL rt key value', function() + wshada('\005\000\009\130\162rX\192\162rt\192') + eq('Vim(rshada):E575: Error while reading ShaDa file: register entry at position 0 has rt key value which is not an unsigned integer', exc_exec(sdrcmd())) + end) + + it('fails on register item with NIL rw key value', function() + wshada('\005\000\009\130\162rX\192\162rw\192') + eq('Vim(rshada):E575: Error while reading ShaDa file: register entry at position 0 has rw key value which is not an unsigned integer', exc_exec(sdrcmd())) + end) + + it('fails on register item with NIL rc key value', function() + wshada('\005\000\009\130\162rX\192\162rc\192') + eq('Vim(rshada):E575: Error while reading ShaDa file: register entry at position 0 has rc key with non-array value', exc_exec(sdrcmd())) + end) + + it('fails on register item with empty rc key value', function() + wshada('\005\000\009\130\162rX\192\162rc\144') + eq('Vim(rshada):E575: Error while reading ShaDa file: register entry at position 0 has rc key with empty array', exc_exec(sdrcmd())) + end) + + it('fails on register item with NIL in rc array', function() + wshada('\005\000\013\130\162rX\192\162rc\146\196\001a\192') + eq('Vim(rshada):E575: Error while reading ShaDa file: register entry at position 0 has rc array with non-binary value', exc_exec(sdrcmd())) + end) + + it('fails on register item without rc array', function() + wshada('\005\000\009\129\162rX\146\196\001a\192') + eq('Vim(rshada):E575: Error while reading ShaDa file: register entry at position 0 has missing rc array', exc_exec(sdrcmd())) + end) + + it('fails on history item with NIL value', function() + wshada('\004\000\001\192') + eq('Vim(rshada):E575: Error while reading ShaDa file: history entry at position 0 is not an array', exc_exec(sdrcmd())) + end) + + it('fails on history item with empty value', function() + wshada('\004\000\001\144') + eq('Vim(rshada):E575: Error while reading ShaDa file: history entry at position 0 does not have enough elements', exc_exec(sdrcmd())) + end) + + it('fails on history item with single element value', function() + wshada('\004\000\002\145\000') + eq('Vim(rshada):E575: Error while reading ShaDa file: history entry at position 0 does not have enough elements', exc_exec(sdrcmd())) + end) + + it('fails on history item with NIL first item', function() + wshada('\004\000\003\146\192\000') + eq('Vim(rshada):E575: Error while reading ShaDa file: history entry at position 0 has wrong history type type', exc_exec(sdrcmd())) + end) + + it('fails on history item with FIXUINT second item', function() + wshada('\004\000\003\146\000\000') + eq('Vim(rshada):E575: Error while reading ShaDa file: history entry at position 0 has wrong history string type', exc_exec(sdrcmd())) + end) + + it('fails on history item with second item with zero byte', function() + wshada('\004\000\007\146\000\196\003ab\000') + eq('Vim(rshada):E575: Error while reading ShaDa file: history entry at position 0 contains string with zero byte inside', exc_exec(sdrcmd())) + end) + + it('fails on search history item without third item', function() + wshada('\004\000\007\146\001\196\003abc') + eq('Vim(rshada):E575: Error while reading ShaDa file: search history entry at position 0 does not have separator character', exc_exec(sdrcmd())) + end) + + it('fails on search history item with NIL third item', function() + wshada('\004\000\007\147\001\196\002ab\192') + eq('Vim(rshada):E575: Error while reading ShaDa file: search history entry at position 0 has wrong history separator type', exc_exec(sdrcmd())) + end) + + it('fails on variable item with NIL value', function() + wshada('\006\000\001\192') + eq('Vim(rshada):E575: Error while reading ShaDa file: variable entry at position 0 is not an array', exc_exec(sdrcmd())) + end) + + it('fails on variable item with empty value', function() + wshada('\006\000\001\144') + eq('Vim(rshada):E575: Error while reading ShaDa file: variable entry at position 0 does not have enough elements', exc_exec(sdrcmd())) + end) + + it('fails on variable item with single element value', function() + wshada('\006\000\002\145\000') + eq('Vim(rshada):E575: Error while reading ShaDa file: variable entry at position 0 does not have enough elements', exc_exec(sdrcmd())) + end) + + it('fails on variable item with NIL first item', function() + wshada('\006\000\003\146\192\000') + eq('Vim(rshada):E575: Error while reading ShaDa file: variable entry at position 0 has wrong variable name type', exc_exec(sdrcmd())) + end) + + it('fails on replacement item with NIL value', function() + wshada('\003\000\001\192') + eq('Vim(rshada):E575: Error while reading ShaDa file: sub string entry at position 0 is not an array', exc_exec(sdrcmd())) + end) + + it('fails on replacement item with empty value', function() + wshada('\003\000\001\144') + eq('Vim(rshada):E575: Error while reading ShaDa file: sub string entry at position 0 does not have enough elements', exc_exec(sdrcmd())) + end) + + it('fails on replacement item with NIL first item', function() + wshada('\003\000\002\145\192') + eq('Vim(rshada):E575: Error while reading ShaDa file: sub string entry at position 0 has wrong sub string type', exc_exec(sdrcmd())) + end) + + it('fails on buffer list item with NIL value', function() + nvim_command('set shada+=%') + wshada('\009\000\001\192') + eq('Vim(rshada):E575: Error while reading ShaDa file: buffer list entry at position 0 is not an array', exc_exec(sdrcmd())) + end) + + it('fails on buffer list item with NIL item in the array', function() + nvim_command('set shada+=%') + wshada('\009\000\008\146\129\161f\196\001/\192') + eq('Vim(rshada):E575: Error while reading ShaDa file: buffer list at position 0 contains entry that is not a dictionary', exc_exec(sdrcmd())) + end) + + it('fails on buffer list item with empty item', function() + nvim_command('set shada+=%') + wshada('\009\000\008\146\129\161f\196\001/\128') + eq('Vim(rshada):E575: Error while reading ShaDa file: buffer list at position 0 contains entry that does not have a file name', exc_exec(sdrcmd())) + end) + + it('fails on buffer list item with NIL l key', function() + nvim_command('set shada+=%') + wshada('\009\000\017\146\129\161f\196\001/\130\161f\196\002/a\161l\192') + eq('Vim(rshada):E575: Error while reading ShaDa file: buffer list entry entry at position 0 has l key value which is not an integer', exc_exec(sdrcmd())) + end) + + it('fails on buffer list item with zero l key', function() + nvim_command('set shada+=%') + wshada('\009\000\017\146\129\161f\196\001/\130\161f\196\002/a\161l\000') + eq('Vim(rshada):E575: Error while reading ShaDa file: buffer list at position 0 contains entry with invalid line number', exc_exec(sdrcmd())) + end) + + it('fails on buffer list item with negative l key', function() + nvim_command('set shada+=%') + wshada('\009\000\017\146\129\161f\196\001/\130\161f\196\002/a\161l\255') + eq('Vim(rshada):E575: Error while reading ShaDa file: buffer list at position 0 contains entry with invalid line number', exc_exec(sdrcmd())) + end) + + it('fails on buffer list item with negative c key', function() + nvim_command('set shada+=%') + wshada('\009\000\017\146\129\161f\196\001/\130\161f\196\002/a\161c\255') + eq('Vim(rshada):E575: Error while reading ShaDa file: buffer list at position 0 contains entry with invalid column number', exc_exec(sdrcmd())) + end) + + it('fails on buffer list item with NIL c key', function() + nvim_command('set shada+=%') + wshada('\009\000\017\146\129\161f\196\001/\130\161f\196\002/a\161c\192') + eq('Vim(rshada):E575: Error while reading ShaDa file: buffer list entry entry at position 0 has c key value which is not an integer', exc_exec(sdrcmd())) + end) + + it('fails on invalid ShaDa file (viminfo file)', function() + wshada([[# This viminfo file was generated by Vim 7.4. +# You may edit it if you're careful! + +# Value of 'encoding' when this file was written +*encoding=utf-8 + + +# hlsearch on (H) or off (h): +~h +# Last Search Pattern: +~MSle0~/buffer=abuf + +# Last Substitute Search Pattern: +~MSle0&^$ + +# Last Substitute String: +$ + +# Command Line History (newest to oldest): +:cq + +# Search String History (newest to oldest): +? \<TMUX\> + +# Expression History (newest to oldest): +=system('echo "\xAB"') + +# Input Line History (newest to oldest): +@i + +# Input Line History (newest to oldest): + +# Registers: +"0 LINE 0 + case FLAG_B: puts("B"); break; +"1 LINE 0 + pick 874a489 shada,functests: Test compatibility support +""- CHAR 0 + . + +# global variables: +!STUF_HISTORY_TRANSLIT LIS [] +!TR3_INPUT_HISTORY LIS [] + +# File marks: +'A 8320 12 ~/a.a/Proj/c/neovim-2076/src/nvim/ex_docmd.c +'0 66 5 ~/a.a/Proj/c/neovim/.git/rebase-merge/git-rebase-todo +'1 7 0 ~/.vam/powerline/.git/MERGE_MSG +'2 64 4 ~/a.a/Proj/c/neovim/.git/rebase-merge/git-rebase-todo +'3 9 0 ~/a.a/Proj/c/neovim/.git/COMMIT_EDITMSG +'4 62 0 ~/a.a/Proj/c/neovim/.git/rebase-merge/git-rebase-todo +'5 57 4 ~/a.a/Proj/c/neovim/.git/rebase-merge/git-rebase-todo +'6 1 0 ~/a.a/Proj/c/neovim/.git/rebase-merge/git-rebase-todo +'7 399 7 /usr/share/vim/vim74/doc/motion.txt +'8 1 0 ~/a.a/Proj/c/zpython/build/CMakeFiles/3.2.2/CMakeCCompiler.cmake +'9 1 0 ~/a.a/Proj/c/vim/README.txt + +# Jumplist (newest first): +-' 66 5 ~/a.a/Proj/c/neovim/.git/rebase-merge/git-rebase-todo + +# History of marks within files (newest to oldest): + +> ~/a.a/Proj/c/neovim/.git/rebase-merge/git-rebase-todo + " 66 5 + ^ 66 6 + . 66 5 + + 65 0 + + 65 0 +]]) + eq('Vim(rshada):E576: Failed to parse ShaDa file: extra bytes in msgpack string at position 3', exc_exec(sdrcmd())) + eq('Vim(wshada):E576: Failed to parse ShaDa file: extra bytes in msgpack string at position 3', exc_exec('wshada ' .. shada_fname)) + eq(0, exc_exec('wshada! ' .. shada_fname)) + end) + + it('fails on invalid ShaDa file (wrapper script)', function() + wshada('#!/bin/sh\n\npowerline "$@" 2>&1 | tee -a powerline\n') + eq('Vim(rshada):E576: Failed to parse ShaDa file: extra bytes in msgpack string at position 3', exc_exec(sdrcmd())) + eq('Vim(wshada):E576: Failed to parse ShaDa file: extra bytes in msgpack string at position 3', exc_exec('wshada ' .. shada_fname)) + eq(0, exc_exec('wshada! ' .. shada_fname)) + end) + + it('fails on invalid ShaDa file (failing skip in second item)', function() + wshada('\001\000\001\128#!/') + eq('Vim(rshada):E576: Error while reading ShaDa file: last entry specified that it occupies 47 bytes, but file ended earlier', exc_exec(sdrcmd())) + eq('Vim(wshada):E576: Error while reading ShaDa file: last entry specified that it occupies 47 bytes, but file ended earlier', exc_exec('wshada ' .. shada_fname)) + eq(0, exc_exec('wshada! ' .. shada_fname)) + end) +end) diff --git a/test/functional/shada/helpers.lua b/test/functional/shada/helpers.lua new file mode 100644 index 0000000000..c2ff4cadd1 --- /dev/null +++ b/test/functional/shada/helpers.lua @@ -0,0 +1,94 @@ +local helpers = require('test.functional.helpers') +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 tmpname = os.tmpname() +local additional_cmd = '' + +local function nvim_argv() + local ret + local nvim_argv = {nvim_prog, '-u', 'NONE', '-i', tmpname, '-N', + '--cmd', 'set shortmess+=I background=light noswapfile', + '--cmd', additional_cmd, + '--embed'} + if helpers.prepend_argv then + return merge_args(helpers.prepend_argv, nvim_argv) + else + return nvim_argv + end +end + +local session = nil + +local reset = function() + if session then + session:exit(0) + end + session = spawn(nvim_argv()) + set_session(session) + meths.set_var('tmpname', tmpname) +end + +local set_additional_cmd = function(s) + additional_cmd = s +end + +local clear = function() + os.remove(tmpname) + set_additional_cmd('') +end + +local get_shada_rw = function(fname) + local wshada = function(text) + write_file(fname, text, true) + end + local sdrcmd = function(bang) + return 'rshada' .. (bang and '!' or '') .. ' ' .. fname + end + local clean = function() + os.remove(fname) + local i = ('a'):byte() + while i <= ('z'):byte() do + if not os.remove(fname .. ('.tmp.%c'):format(i)) then + break + end + i = i + 1 + end + end + return wshada, sdrcmd, fname, clean +end + +local mpack_keys = {'type', 'timestamp', 'length', 'value'} + +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 ret = {} + local cur + local i = 0 + while true do + local off, val = unpacker() + if not off then break end + if i % 4 == 0 then + cur = {} + ret[#ret + 1] = cur + end + cur[mpack_keys[(i % 4) + 1]] = val + i = i + 1 + end + return ret +end + +return { + reset=reset, + set_additional_cmd=set_additional_cmd, + clear=clear, + exc_exec=exc_exec, + get_shada_rw=get_shada_rw, + read_shada_file=read_shada_file, +} diff --git a/test/functional/shada/history_spec.lua b/test/functional/shada/history_spec.lua new file mode 100644 index 0000000000..1123f829d2 --- /dev/null +++ b/test/functional/shada/history_spec.lua @@ -0,0 +1,312 @@ +-- ShaDa history saving/reading support +local helpers = require('test.functional.helpers') +local nvim_command, funcs, meths, nvim_feed, eq = + helpers.command, helpers.funcs, helpers.meths, helpers.feed, helpers.eq + +local shada_helpers = require('test.functional.shada.helpers') +local reset, set_additional_cmd, clear = + shada_helpers.reset, shada_helpers.set_additional_cmd, + shada_helpers.clear + +describe('ShaDa support code', function() + before_each(reset) + after_each(clear) + + it('is able to dump and read back command-line history', function() + nvim_command('set shada=\'0') + nvim_feed(':" Test\n') + nvim_command('wshada') + reset() + nvim_command('set shada=\'0') + nvim_command('rshada') + eq('" Test', funcs.histget(':', -1)) + end) + + it('is able to dump and read back 2 items in command-line history', function() + nvim_command('set shada=\'0 history=2') + nvim_feed(':" Test\n') + nvim_feed(':" Test 2\n') + nvim_command('qall') + reset() + nvim_command('set shada=\'0 history=2') + nvim_command('rshada') + eq('" Test 2', funcs.histget(':', -1)) + eq('" Test', funcs.histget(':', -2)) + nvim_command('qall') + end) + + it('respects &history when dumping', + function() + nvim_command('set shada=\'0 history=1') + nvim_feed(':" Test\n') + nvim_feed(':" Test 2\n') + nvim_command('wshada') + reset() + nvim_command('set shada=\'0 history=2') + nvim_command('rshada') + eq('" Test 2', funcs.histget(':', -1)) + eq('', funcs.histget(':', -2)) + end) + + it('respects &history when loading', + function() + nvim_command('set shada=\'0 history=2') + nvim_feed(':" Test\n') + nvim_feed(':" Test 2\n') + nvim_command('wshada') + reset() + nvim_command('set shada=\'0 history=1') + nvim_command('rshada') + eq('" Test 2', funcs.histget(':', -1)) + eq('', funcs.histget(':', -2)) + end) + + it('dumps only requested amount of command-line history items', function() + nvim_command('set shada=\'0,:1') + nvim_feed(':" Test\n') + nvim_feed(':" Test 2\n') + nvim_command('wshada') + -- Regression test: :wshada should not alter or free history. + eq('" Test 2', funcs.histget(':', -1)) + eq('" Test', funcs.histget(':', -2)) + reset() + nvim_command('set shada=\'0') + nvim_command('rshada') + eq('" Test 2', funcs.histget(':', -1)) + eq('', funcs.histget(':', -2)) + end) + + it('does not respect number in &shada when loading history', function() + nvim_command('set shada=\'0') + nvim_feed(':" Test\n') + nvim_feed(':" Test 2\n') + nvim_command('wshada') + reset() + nvim_command('set shada=\'0,:1') + nvim_command('rshada') + eq('" Test 2', funcs.histget(':', -1)) + eq('" Test', funcs.histget(':', -2)) + end) + + it('dumps and loads all kinds of histories', function() + nvim_command('debuggreedy') + nvim_feed(':debug echo "Test"\n" Test 2\nc\n') -- Debug history. + nvim_feed(':call input("")\nTest 2\n') -- Input history. + nvim_feed('"="Test"\nyy') -- Expression history. + nvim_feed('/Test\n') -- Search history + nvim_feed(':" Test\n') -- Command-line history + nvim_command('0debuggreedy') + nvim_command('wshada') + reset() + nvim_command('rshada') + eq('" Test', funcs.histget(':', -1)) + eq('Test', funcs.histget('/', -1)) + eq('"Test"', funcs.histget('=', -1)) + eq('Test 2', funcs.histget('@', -1)) + eq('c', funcs.histget('>', -1)) + end) + + it('dumps and loads last search pattern with offset', function() + funcs.setline('.', {'foo', 'bar'}) + nvim_feed('gg0/a/e+1\n') + eq({0, 2, 3, 0}, funcs.getpos('.')) + nvim_command('wshada') + reset() + funcs.setline('.', {'foo', 'bar'}) + nvim_feed('gg0n') + eq({0, 2, 3, 0}, funcs.getpos('.')) + end) + + it('saves v:hlsearch=1', function() + nvim_command('set hlsearch shada-=h') + nvim_feed('/test\n') + eq(1, meths.get_vvar('hlsearch')) + nvim_command('qall') + reset() + eq(1, meths.get_vvar('hlsearch')) + end) + + it('saves v:hlsearch=0 with :nohl', function() + nvim_command('set hlsearch shada-=h') + nvim_feed('/test\n') + nvim_command('nohlsearch') + nvim_command('qall') + reset() + eq(0, meths.get_vvar('hlsearch')) + end) + + it('saves v:hlsearch=0 with default &shada', function() + nvim_command('set hlsearch') + nvim_feed('/test\n') + eq(1, meths.get_vvar('hlsearch')) + nvim_command('qall') + reset() + eq(0, meths.get_vvar('hlsearch')) + end) + + it('dumps and loads last substitute pattern and replacement string', function() + funcs.setline('.', {'foo', 'bar'}) + nvim_command('%s/f/g/g') + eq('goo', funcs.getline(1)) + nvim_command('wshada') + reset() + funcs.setline('.', {'foo', 'bar'}) + nvim_command('&') + eq('goo', funcs.getline(1)) + end) + + it('dumps and loads history correctly when &encoding is not UTF-8', function() + set_additional_cmd('set encoding=latin1') + reset() + -- \171 is U+00AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK in latin1 + nvim_feed(':echo "\171"\n') + nvim_command('qall') + reset() + eq('echo "\171"', funcs.histget(':', -1)) + end) + + it('dumps and loads history correctly when &encoding /= UTF-8 when dumping', + function() + set_additional_cmd('set encoding=latin1') + reset() + -- \171 is U+00AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK in latin1 + nvim_feed(':echo "\171"\n') + set_additional_cmd('') + nvim_command('qall') + reset() + eq('echo "«"', funcs.histget(':', -1)) + end) + + it('dumps and loads history correctly when &encoding /= UTF-8 when loading', + function() + -- \171 is U+00AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK in latin1 + nvim_feed(':echo "«"\n') + set_additional_cmd('set encoding=latin1') + nvim_command('qall') + reset() + eq('echo "\171"', funcs.histget(':', -1)) + end) + + it('dumps and loads replacement correctly when &encoding is not UTF-8', + function() + set_additional_cmd('set encoding=latin1') + reset() + -- \171 is U+00AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK in latin1 + nvim_command('substitute/./\171/ge') + nvim_command('qall!') + reset() + funcs.setline('.', {'.'}) + nvim_command('&') + eq('\171', funcs.getline('.')) + end) + + it('dumps&loads replacement correctly when &encoding /= UTF-8 when dumping', + function() + set_additional_cmd('set encoding=latin1') + reset() + -- \171 is U+00AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK in latin1 + nvim_command('substitute/./\171/ge') + set_additional_cmd('') + nvim_command('qall') + reset() + funcs.setline('.', {'.'}) + nvim_command('&') + eq('«', funcs.getline('.')) + end) + + it('dumps&loads replacement correctly when &encoding /= UTF-8 when loading', + function() + -- \171 is U+00AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK in latin1 + nvim_command('substitute/./«/ge') + set_additional_cmd('set encoding=latin1') + nvim_command('qall') + reset() + funcs.setline('.', {'.'}) + nvim_command('&') + eq('\171', funcs.getline('.')) + end) + + it('dumps and loads substitute pattern correctly when &encoding is not UTF-8', + function() + set_additional_cmd('set encoding=latin1') + reset() + -- \171 is U+00AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK in latin1 + nvim_command('substitute/\171/./ge') + nvim_command('qall!') + reset() + funcs.setline('.', {'\171«'}) + nvim_command('&') + eq('.«', funcs.getline('.')) + end) + + it('dumps&loads s/pattern correctly when &encoding /= UTF-8 when dumping', + function() + set_additional_cmd('set encoding=latin1') + reset() + -- \171 is U+00AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK in latin1 + nvim_command('substitute/\171/./ge') + set_additional_cmd('') + nvim_command('qall') + reset() + funcs.setline('.', {'«\171'}) + nvim_command('&') + eq('.\171', funcs.getline('.')) + end) + + it('dumps&loads s/pattern correctly when &encoding /= UTF-8 when loading', + function() + -- \171 is U+00AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK in latin1 + nvim_command('substitute/«/./ge') + set_additional_cmd('set encoding=latin1') + nvim_command('qall') + reset() + funcs.setline('.', {'\171«'}) + nvim_command('&') + eq('.«', funcs.getline('.')) + end) + + it('dumps and loads search pattern correctly when &encoding is not UTF-8', + function() + set_additional_cmd('set encoding=latin1') + reset() + -- \171 is U+00AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK in latin1 + nvim_command('silent! /\171/') + nvim_command('set shada+=/0') + nvim_command('qall!') + reset() + funcs.setline('.', {'\171«'}) + nvim_command('~&') + eq('«', funcs.getline('.')) + eq('', funcs.histget('/', -1)) + end) + + it('dumps&loads /pattern correctly when &encoding /= UTF-8 when dumping', + function() + set_additional_cmd('set encoding=latin1') + reset() + -- \171 is U+00AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK in latin1 + nvim_command('silent! /\171/') + nvim_command('set shada+=/0') + set_additional_cmd('') + nvim_command('qall') + reset() + funcs.setline('.', {'«\171'}) + nvim_command('~&') + eq('\171', funcs.getline('.')) + eq('', funcs.histget('/', -1)) + end) + + it('dumps&loads /pattern correctly when &encoding /= UTF-8 when loading', + function() + -- \171 is U+00AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK in latin1 + nvim_command('silent! /«/') + nvim_command('set shada+=/0') + set_additional_cmd('set encoding=latin1') + nvim_command('qall') + reset() + funcs.setline('.', {'\171«'}) + nvim_command('~&') + eq('«', funcs.getline('.')) + eq('', funcs.histget('/', -1)) + end) +end) diff --git a/test/functional/shada/marks_spec.lua b/test/functional/shada/marks_spec.lua new file mode 100644 index 0000000000..b03af39662 --- /dev/null +++ b/test/functional/shada/marks_spec.lua @@ -0,0 +1,180 @@ +-- ShaDa marks saving/reading support +local helpers = require('test.functional.helpers') +local meths, curwinmeths, curbufmeths, nvim_command, funcs, eq = + helpers.meths, helpers.curwinmeths, helpers.curbufmeths, helpers.command, + helpers.funcs, helpers.eq +local exc_exec, redir_exec = helpers.exc_exec, helpers.redir_exec + +local shada_helpers = require('test.functional.shada.helpers') +local reset, set_additional_cmd, clear = + shada_helpers.reset, shada_helpers.set_additional_cmd, + shada_helpers.clear + +local nvim_current_line = function() + return curwinmeths.get_cursor()[1] +end + +describe('ShaDa support code', function() + testfilename = 'Xtestfile-functional-shada-marks' + testfilename_2 = 'Xtestfile-functional-shada-marks-2' + before_each(function() + reset() + local fd = io.open(testfilename, 'w') + fd:write('test\n') + fd:write('test2\n') + fd:close() + local fd = io.open(testfilename_2, 'w') + fd:write('test3\n') + fd:write('test4\n') + fd:close() + end) + after_each(function() + clear() + os.remove(testfilename) + os.remove(testfilename_2) + end) + + it('is able to dump and read back global mark', function() + nvim_command('edit ' .. testfilename) + nvim_command('mark A') + nvim_command('2') + nvim_command('kB') + nvim_command('wshada') + reset() + nvim_command('rshada') + nvim_command('normal! `A') + eq(testfilename, funcs.fnamemodify(curbufmeths.get_name(), ':t')) + eq(1, nvim_current_line()) + nvim_command('normal! `B') + eq(2, nvim_current_line()) + end) + + it('does not dump global mark with `f0` in shada', function() + nvim_command('set shada+=f0') + nvim_command('edit ' .. testfilename) + nvim_command('mark A') + nvim_command('2') + nvim_command('kB') + nvim_command('wshada') + reset() + nvim_command('language C') + eq('Vim(normal):E20: Mark not set', exc_exec('normal! `A')) + end) + + it('does read back global mark even with `\'0` and `f0` in shada', function() + nvim_command('edit ' .. testfilename) + nvim_command('mark A') + nvim_command('2') + nvim_command('kB') + nvim_command('wshada') + set_additional_cmd('set shada=\'0,f0') + reset() + nvim_command('language C') + nvim_command('normal! `A') + eq(testfilename, funcs.fnamemodify(curbufmeths.get_name(), ':t')) + eq(1, nvim_current_line()) + end) + + it('is able to dump and read back local mark', function() + nvim_command('edit ' .. testfilename) + nvim_command('mark a') + nvim_command('2') + nvim_command('kb') + nvim_command('qall') + reset() + nvim_command('edit ' .. testfilename) + nvim_command('normal! `a') + eq(testfilename, funcs.fnamemodify(curbufmeths.get_name(), ':t')) + eq(1, nvim_current_line()) + nvim_command('normal! `b') + eq(2, nvim_current_line()) + end) + + it('is able to populate v:oldfiles', function() + nvim_command('edit ' .. testfilename) + local tf_full = curbufmeths.get_name() + nvim_command('edit ' .. testfilename_2) + local tf_full_2 = curbufmeths.get_name() + nvim_command('qall') + reset() + local oldfiles = meths.get_vvar('oldfiles') + eq(2, #oldfiles) + eq(testfilename, oldfiles[1]:sub(-#testfilename)) + eq(testfilename_2, oldfiles[2]:sub(-#testfilename_2)) + eq(tf_full, oldfiles[1]) + eq(tf_full_2, oldfiles[2]) + nvim_command('rshada!') + local oldfiles = meths.get_vvar('oldfiles') + eq(2, #oldfiles) + eq(testfilename, oldfiles[1]:sub(-#testfilename)) + eq(testfilename_2, oldfiles[2]:sub(-#testfilename_2)) + eq(tf_full, oldfiles[1]) + eq(tf_full_2, oldfiles[2]) + end) + + it('is able to dump and restore jump list', function() + nvim_command('edit ' .. testfilename_2) + nvim_command('normal! G') + nvim_command('normal! gg') + nvim_command('edit ' .. testfilename) + nvim_command('normal! G') + nvim_command('normal! gg') + nvim_command('enew') + nvim_command('normal! gg') + local saved = redir_exec('jumps') + nvim_command('qall') + reset() + eq(saved, redir_exec('jumps')) + end) + + it('is able to dump and restore jump list with different times (slow!)', + function() + nvim_command('edit ' .. testfilename_2) + nvim_command('sleep 2') + nvim_command('normal! G') + nvim_command('sleep 2') + nvim_command('normal! gg') + nvim_command('sleep 2') + nvim_command('edit ' .. testfilename) + nvim_command('sleep 2') + nvim_command('normal! G') + nvim_command('sleep 2') + nvim_command('normal! gg') + nvim_command('qall') + reset() + nvim_command('redraw') + nvim_command('edit ' .. testfilename) + eq(testfilename, funcs.bufname('%')) + eq(1, nvim_current_line()) + nvim_command('execute "normal! \\<C-o>"') + eq(testfilename, funcs.bufname('%')) + eq(1, nvim_current_line()) + nvim_command('execute "normal! \\<C-o>"') + eq(testfilename, funcs.bufname('%')) + eq(2, nvim_current_line()) + nvim_command('execute "normal! \\<C-o>"') + eq(testfilename_2, funcs.bufname('%')) + eq(1, nvim_current_line()) + nvim_command('execute "normal! \\<C-o>"') + eq(testfilename_2, funcs.bufname('%')) + eq(2, nvim_current_line()) + end) + + it('is able to dump and restore change list', function() + nvim_command('edit ' .. testfilename) + nvim_command('normal! Gra') + nvim_command('normal! ggrb') + nvim_command('qall!') + reset() + nvim_command('edit ' .. testfilename) + nvim_command('normal! Gg;') + -- Note: without “sync” “commands” test has good changes to fail for unknown + -- reason (in first eq expected 1 is compared with 2). Any command inserted + -- causes this to work properly. + nvim_command('" sync') + eq(1, nvim_current_line()) + nvim_command('normal! g;') + nvim_command('" sync 2') + eq(2, nvim_current_line()) + end) +end) diff --git a/test/functional/shada/merging_spec.lua b/test/functional/shada/merging_spec.lua new file mode 100644 index 0000000000..7066ca9f54 --- /dev/null +++ b/test/functional/shada/merging_spec.lua @@ -0,0 +1,1032 @@ +-- ShaDa merging data support +local helpers = require('test.functional.helpers') +local nvim_command, meths, funcs, curbufmeths, eq = + helpers.command, helpers.meths, helpers.funcs, + helpers.curbufmeths, helpers.eq +local exc_exec, redir_exec = helpers.exc_exec, helpers.redir_exec + +local shada_helpers = require('test.functional.shada.helpers') +local reset, clear, get_shada_rw = + shada_helpers.reset, shada_helpers.clear, shada_helpers.get_shada_rw +local read_shada_file = shada_helpers.read_shada_file + +local wshada, sdrcmd, shada_fname = + get_shada_rw('Xtest-functional-shada-merging.shada') + +describe('ShaDa history merging code', function() + before_each(reset) + after_each(function() + clear() + os.remove(shada_fname) + end) + + it('takes item with greater timestamp from Neovim instance when reading', + function() + wshada('\004\001\009\147\000\196\002ab\196\001a') + eq(0, exc_exec(sdrcmd())) + wshada('\004\000\009\147\000\196\002ab\196\001b') + eq(0, exc_exec(sdrcmd())) + os.remove(shada_fname) + eq(0, exc_exec('wshada! ' .. shada_fname)) + local found = 0 + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 4 and v.value[1] == 0 and v.value[2] == 'ab' then + eq(1, v.timestamp) + eq('a', v.value[3]) + found = found + 1 + end + end + eq(1, found) + end) + + it('takes item with equal timestamp from Neovim instance when reading', + function() + wshada('\004\000\009\147\000\196\002ab\196\001a') + eq(0, exc_exec(sdrcmd())) + wshada('\004\000\009\147\000\196\002ab\196\001b') + eq(0, exc_exec(sdrcmd())) + os.remove(shada_fname) + eq(0, exc_exec('wshada! ' .. shada_fname)) + local found = 0 + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 4 and v.value[1] == 0 and v.value[2] == 'ab' then + eq(0, v.timestamp) + eq('a', v.value[3]) + found = found + 1 + end + end + eq(1, found) + end) + + it('takes item with greater timestamp from ShaDa when reading', + function() + wshada('\004\000\009\147\000\196\002ab\196\001a') + eq(0, exc_exec(sdrcmd())) + wshada('\004\001\009\147\000\196\002ab\196\001b') + eq(0, exc_exec(sdrcmd())) + os.remove(shada_fname) + eq(0, exc_exec('wshada! ' .. shada_fname)) + local found = 0 + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 4 and v.value[1] == 0 and v.value[2] == 'ab' then + eq(1, v.timestamp) + eq('b', v.value[3]) + found = found + 1 + end + end + eq(1, found) + end) + + it('takes item with greater timestamp from Neovim instance when writing', + function() + wshada('\004\001\009\147\000\196\002ab\196\001a') + eq(0, exc_exec(sdrcmd())) + wshada('\004\000\009\147\000\196\002ab\196\001b') + eq(0, exc_exec('wshada ' .. shada_fname)) + local found = 0 + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 4 and v.value[1] == 0 and v.value[2] == 'ab' then + eq(1, v.timestamp) + eq('a', v.value[3]) + found = found + 1 + end + end + eq(1, found) + end) + + it('takes item with equal timestamp from Neovim instance when writing', + function() + wshada('\004\000\009\147\000\196\002ab\196\001a') + eq(0, exc_exec(sdrcmd())) + wshada('\004\000\009\147\000\196\002ab\196\001b') + eq(0, exc_exec('wshada ' .. shada_fname)) + local found = 0 + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 4 and v.value[1] == 0 and v.value[2] == 'ab' then + eq(0, v.timestamp) + eq('a', v.value[3]) + found = found + 1 + end + end + eq(1, found) + end) + + it('takes item with greater timestamp from ShaDa when writing', + function() + wshada('\004\000\009\147\000\196\002ab\196\001a') + eq(0, exc_exec(sdrcmd())) + wshada('\004\001\009\147\000\196\002ab\196\001b') + eq(0, exc_exec('wshada ' .. shada_fname)) + local found = 0 + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 4 and v.value[1] == 0 and v.value[2] == 'ab' then + eq(1, v.timestamp) + eq('b', v.value[3]) + found = found + 1 + end + end + eq(1, found) + end) + + it('correctly reads history items with messed up timestamps', + function() + wshada('\004\010\009\147\000\196\002ab\196\001a' + .. '\004\010\009\147\000\196\002ac\196\001a' + .. '\004\005\009\147\000\196\002ad\196\001a' + .. '\004\100\009\147\000\196\002ae\196\001a' + .. '\004\090\009\147\000\196\002af\196\001a' + ) + eq(0, exc_exec(sdrcmd())) + os.remove(shada_fname) + eq(0, exc_exec('wshada! ' .. shada_fname)) + local items = {'ad', 'ab', 'ac', 'af', 'ae'} + for i, v in ipairs(items) do + eq(v, funcs.histget(':', i)) + end + local found = 0 + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 4 and v.value[1] == 0 then + found = found + 1 + eq(items[found], v.value[2]) + eq('a', v.value[3]) + end + end + eq(#items, found) + end) + + it('correctly reorders history items with messed up timestamps when writing', + function() + wshada('\004\010\009\147\000\196\002ab\196\001a' + .. '\004\010\009\147\000\196\002ac\196\001a' + .. '\004\005\009\147\000\196\002ad\196\001a' + .. '\004\100\009\147\000\196\002ae\196\001a' + .. '\004\090\009\147\000\196\002af\196\001a' + ) + eq(0, exc_exec('wshada ' .. shada_fname)) + local items = {'ad', 'ab', 'ac', 'af', 'ae'} + local found = 0 + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 4 and v.value[1] == 0 then + found = found + 1 + eq(items[found], v.value[2]) + eq('a', v.value[3]) + end + end + eq(#items, found) + end) + + it('correctly merges history items with duplicate mid entry when writing', + function() + -- Regression test: ShaDa code used to crash here. + -- Conditions: + -- 1. Entry which is duplicate to non-last entry. + -- 2. At least one more non-duplicate entry. + wshada('\004\000\009\147\000\196\002ab\196\001a' + .. '\004\001\009\147\000\196\002ac\196\001a' + .. '\004\002\009\147\000\196\002ad\196\001a' + .. '\004\003\009\147\000\196\002ac\196\001a' + .. '\004\004\009\147\000\196\002af\196\001a' + .. '\004\005\009\147\000\196\002ae\196\001a' + .. '\004\006\009\147\000\196\002ag\196\001a' + .. '\004\007\009\147\000\196\002ah\196\001a' + .. '\004\008\009\147\000\196\002ai\196\001a' + ) + eq(0, exc_exec('wshada ' .. shada_fname)) + local items = {'ab', 'ad', 'ac', 'af', 'ae', 'ag', 'ah', 'ai'} + local found = 0 + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 4 and v.value[1] == 0 then + found = found + 1 + eq(items[found], v.value[2]) + eq('a', v.value[3]) + end + end + eq(#items, found) + end) + + it('correctly merges history items with duplicate adj entry when writing', + function() + wshada('\004\000\009\147\000\196\002ab\196\001a' + .. '\004\001\009\147\000\196\002ac\196\001a' + .. '\004\002\009\147\000\196\002ad\196\001a' + .. '\004\003\009\147\000\196\002ad\196\001a' + .. '\004\004\009\147\000\196\002af\196\001a' + .. '\004\005\009\147\000\196\002ae\196\001a' + .. '\004\006\009\147\000\196\002ag\196\001a' + .. '\004\007\009\147\000\196\002ah\196\001a' + .. '\004\008\009\147\000\196\002ai\196\001a' + ) + eq(0, exc_exec('wshada ' .. shada_fname)) + local items = {'ab', 'ac', 'ad', 'af', 'ae', 'ag', 'ah', 'ai'} + local found = 0 + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 4 and v.value[1] == 0 then + found = found + 1 + eq(items[found], v.value[2]) + eq('a', v.value[3]) + end + end + eq(#items, found) + end) +end) + +describe('ShaDa search pattern support code', function() + before_each(reset) + after_each(function() + clear() + os.remove(shada_fname) + end) + + it('uses last search pattern with gt timestamp from instance when reading', + function() + wshada('\002\001\011\130\162sX\194\162sp\196\001-') + eq(0, exc_exec(sdrcmd())) + wshada('\002\000\011\130\162sX\194\162sp\196\001?') + eq(0, exc_exec(sdrcmd())) + eq('-', funcs.getreg('/')) + end) + + it('uses last search pattern with gt tstamp from file when reading with bang', + function() + wshada('\002\001\011\130\162sX\194\162sp\196\001-') + eq(0, exc_exec(sdrcmd())) + wshada('\002\000\011\130\162sX\194\162sp\196\001?') + eq(0, exc_exec(sdrcmd(true))) + eq('?', funcs.getreg('/')) + end) + + it('uses last search pattern with eq timestamp from instance when reading', + function() + wshada('\002\001\011\130\162sX\194\162sp\196\001-') + eq(0, exc_exec(sdrcmd())) + wshada('\002\001\011\130\162sX\194\162sp\196\001?') + eq(0, exc_exec(sdrcmd())) + eq('-', funcs.getreg('/')) + end) + + it('uses last search pattern with gt timestamp from file when reading', + function() + wshada('\002\001\011\130\162sX\194\162sp\196\001-') + eq(0, exc_exec(sdrcmd())) + wshada('\002\002\011\130\162sX\194\162sp\196\001?') + eq(0, exc_exec(sdrcmd())) + eq('?', funcs.getreg('/')) + end) + + it('uses last search pattern with gt timestamp from instance when writing', + function() + wshada('\002\001\011\130\162sX\194\162sp\196\001-') + eq(0, exc_exec(sdrcmd())) + wshada('\002\000\011\130\162sX\194\162sp\196\001?') + eq('-', funcs.getreg('/')) + eq(0, exc_exec('wshada ' .. shada_fname)) + local found = 0 + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 2 and v.value.sp == '-' then + found = found + 1 + end + end + eq(1, found) + end) + + it('uses last search pattern with eq timestamp from instance when writing', + function() + wshada('\002\001\011\130\162sX\194\162sp\196\001-') + eq(0, exc_exec(sdrcmd())) + wshada('\002\001\011\130\162sX\194\162sp\196\001?') + eq('-', funcs.getreg('/')) + eq(0, exc_exec('wshada ' .. shada_fname)) + local found = 0 + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 2 and v.value.sp == '-' then + found = found + 1 + end + end + eq(1, found) + end) + + it('uses last search pattern with gt timestamp from file when writing', + function() + wshada('\002\001\011\130\162sX\194\162sp\196\001-') + eq(0, exc_exec(sdrcmd())) + wshada('\002\002\011\130\162sX\194\162sp\196\001?') + eq('-', funcs.getreg('/')) + eq(0, exc_exec('wshada ' .. shada_fname)) + local found = 0 + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 2 and v.value.sp == '?' then + found = found + 1 + end + end + eq(1, found) + end) + + it('uses last s/ pattern with gt timestamp from instance when reading', + function() + wshada('\002\001\011\130\162ss\195\162sp\196\001-') + eq(0, exc_exec(sdrcmd())) + wshada('\002\000\011\130\162ss\195\162sp\196\001?') + eq(0, exc_exec(sdrcmd())) + eq('-', funcs.getreg('/')) + end) + + it('uses last s/ pattern with gt timestamp from file when reading with !', + function() + wshada('\002\001\011\130\162ss\195\162sp\196\001-') + eq(0, exc_exec(sdrcmd())) + wshada('\002\000\011\130\162ss\195\162sp\196\001?') + eq(0, exc_exec(sdrcmd(true))) + eq('?', funcs.getreg('/')) + end) + + it('uses last s/ pattern with eq timestamp from instance when reading', + function() + wshada('\002\001\011\130\162ss\195\162sp\196\001-') + eq(0, exc_exec(sdrcmd())) + wshada('\002\001\011\130\162ss\195\162sp\196\001?') + eq(0, exc_exec(sdrcmd())) + eq('-', funcs.getreg('/')) + end) + + it('uses last s/ pattern with gt timestamp from file when reading', + function() + wshada('\002\001\011\130\162ss\195\162sp\196\001-') + eq(0, exc_exec(sdrcmd())) + wshada('\002\002\011\130\162ss\195\162sp\196\001?') + eq(0, exc_exec(sdrcmd())) + eq('?', funcs.getreg('/')) + end) + + it('uses last s/ pattern with gt timestamp from instance when writing', + function() + wshada('\002\001\011\130\162ss\195\162sp\196\001-') + eq(0, exc_exec(sdrcmd())) + wshada('\002\000\011\130\162ss\195\162sp\196\001?') + eq('-', funcs.getreg('/')) + eq(0, exc_exec('wshada ' .. shada_fname)) + local found = 0 + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 2 and v.value.sp == '-' then + found = found + 1 + end + end + eq(1, found) + end) + + it('uses last s/ pattern with eq timestamp from instance when writing', + function() + wshada('\002\001\011\130\162ss\195\162sp\196\001-') + eq(0, exc_exec(sdrcmd())) + wshada('\002\001\011\130\162ss\195\162sp\196\001?') + eq('-', funcs.getreg('/')) + eq(0, exc_exec('wshada ' .. shada_fname)) + local found = 0 + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 2 and v.value.sp == '-' then + found = found + 1 + end + end + eq(1, found) + end) + + it('uses last s/ pattern with gt timestamp from file when writing', + function() + wshada('\002\001\011\130\162ss\195\162sp\196\001-') + eq(0, exc_exec(sdrcmd())) + wshada('\002\002\011\130\162ss\195\162sp\196\001?') + eq('-', funcs.getreg('/')) + eq(0, exc_exec('wshada ' .. shada_fname)) + local found = 0 + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 2 and v.value.sp == '?' then + found = found + 1 + end + end + eq(1, found) + end) +end) + +describe('ShaDa replacement string support code', function() + before_each(reset) + after_each(function() + clear() + os.remove(shada_fname) + end) + + it('uses last replacement with gt timestamp from instance when reading', + function() + wshada('\003\001\004\145\196\001-') + eq(0, exc_exec(sdrcmd())) + wshada('\003\000\004\145\196\001?') + eq(0, exc_exec(sdrcmd())) + nvim_command('s/.*/~') + eq('-', funcs.getline('.')) + nvim_command('bwipeout!') + end) + + it('uses last replacement with gt timestamp from file when reading with bang', + function() + wshada('\003\001\004\145\196\001-') + eq(0, exc_exec(sdrcmd())) + wshada('\003\000\004\145\196\001?') + eq(0, exc_exec(sdrcmd(true))) + nvim_command('s/.*/~') + eq('?', funcs.getline('.')) + nvim_command('bwipeout!') + end) + + it('uses last replacement with eq timestamp from instance when reading', + function() + wshada('\003\001\004\145\196\001-') + eq(0, exc_exec(sdrcmd())) + wshada('\003\001\004\145\196\001?') + eq(0, exc_exec(sdrcmd())) + nvim_command('s/.*/~') + eq('-', funcs.getline('.')) + nvim_command('bwipeout!') + end) + + it('uses last replacement with gt timestamp from file when reading', + function() + wshada('\003\001\004\145\196\001-') + eq(0, exc_exec(sdrcmd())) + wshada('\003\002\004\145\196\001?') + eq(0, exc_exec(sdrcmd())) + nvim_command('s/.*/~') + eq('?', funcs.getline('.')) + nvim_command('bwipeout!') + end) + + it('uses last replacement with gt timestamp from instance when writing', + function() + wshada('\003\001\004\145\196\001-') + eq(0, exc_exec(sdrcmd())) + wshada('\003\000\004\145\196\001?') + eq(0, exc_exec('wshada ' .. shada_fname)) + local found = 0 + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 3 and v.value[1] == '-' then + found = found + 1 + end + end + eq(1, found) + end) + + it('uses last replacement with eq timestamp from instance when writing', + function() + wshada('\003\001\004\145\196\001-') + eq(0, exc_exec(sdrcmd())) + wshada('\003\001\004\145\196\001?') + eq(0, exc_exec('wshada ' .. shada_fname)) + local found = 0 + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 3 and v.value[1] == '-' then + found = found + 1 + end + end + eq(1, found) + end) + + it('uses last replacement with gt timestamp from file when writing', + function() + wshada('\003\001\004\145\196\001-') + eq(0, exc_exec(sdrcmd())) + wshada('\003\002\004\145\196\001?') + eq(0, exc_exec('wshada ' .. shada_fname)) + local found = 0 + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 3 and v.value[1] == '?' then + found = found + 1 + end + end + eq(1, found) + end) +end) + +describe('ShaDa marks support code', function() + before_each(reset) + after_each(function() + clear() + os.remove(shada_fname) + end) + + it('uses last A mark with gt timestamp from instance when reading', + function() + wshada('\007\001\018\131\162mX\195\161f\196\006/a/b/-\161nA') + eq(0, exc_exec(sdrcmd())) + wshada('\007\000\018\131\162mX\195\161f\196\006/a/b/?\161nA') + eq(0, exc_exec(sdrcmd())) + nvim_command('normal! `A') + eq('-', funcs.fnamemodify(curbufmeths.get_name(), ':t')) + end) + + it('uses last A mark with gt timestamp from file when reading with !', + function() + wshada('\007\001\018\131\162mX\195\161f\196\006/a/b/-\161nA') + eq(0, exc_exec(sdrcmd())) + wshada('\007\000\018\131\162mX\195\161f\196\006/a/b/?\161nA') + eq(0, exc_exec(sdrcmd(true))) + nvim_command('normal! `A') + eq('?', funcs.fnamemodify(curbufmeths.get_name(), ':t')) + end) + + it('uses last A mark with eq timestamp from instance when reading', + function() + wshada('\007\001\018\131\162mX\195\161f\196\006/a/b/-\161nA') + eq(0, exc_exec(sdrcmd())) + wshada('\007\001\018\131\162mX\195\161f\196\006/a/b/?\161nA') + eq(0, exc_exec(sdrcmd())) + nvim_command('normal! `A') + eq('-', funcs.fnamemodify(curbufmeths.get_name(), ':t')) + end) + + it('uses last A mark with gt timestamp from file when reading', + function() + wshada('\007\001\018\131\162mX\195\161f\196\006/a/b/-\161nA') + eq(0, exc_exec(sdrcmd())) + wshada('\007\002\018\131\162mX\195\161f\196\006/a/b/?\161nA') + eq(0, exc_exec(sdrcmd())) + nvim_command('normal! `A') + eq('?', funcs.fnamemodify(curbufmeths.get_name(), ':t')) + end) + + it('uses last A mark with gt timestamp from instance when writing', + function() + wshada('\007\001\018\131\162mX\195\161f\196\006/a/b/-\161nA') + eq(0, exc_exec(sdrcmd())) + wshada('\007\000\018\131\162mX\195\161f\196\006/a/b/?\161nA') + nvim_command('normal! `A') + eq('-', funcs.fnamemodify(curbufmeths.get_name(), ':t')) + eq(0, exc_exec('wshada ' .. shada_fname)) + local found = 0 + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 7 and v.value.f == '/a/b/-' then + found = found + 1 + end + end + eq(1, found) + end) + + it('uses last A mark with eq timestamp from instance when writing', + function() + wshada('\007\001\018\131\162mX\195\161f\196\006/a/b/-\161nA') + eq(0, exc_exec(sdrcmd())) + wshada('\007\001\018\131\162mX\195\161f\196\006/a/b/?\161nA') + nvim_command('normal! `A') + eq('-', funcs.fnamemodify(curbufmeths.get_name(), ':t')) + eq(0, exc_exec('wshada ' .. shada_fname)) + local found = 0 + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 7 and v.value.f == '/a/b/-' then + found = found + 1 + end + end + eq(1, found) + end) + + it('uses last A mark with gt timestamp from file when writing', + function() + wshada('\007\001\018\131\162mX\195\161f\196\006/a/b/-\161nA') + eq(0, exc_exec(sdrcmd())) + wshada('\007\002\018\131\162mX\195\161f\196\006/a/b/?\161nA') + nvim_command('normal! `A') + eq('-', funcs.fnamemodify(curbufmeths.get_name(), ':t')) + eq(0, exc_exec('wshada ' .. shada_fname)) + local found = 0 + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 7 and v.value.f == '/a/b/?' then + found = found + 1 + end + end + eq(1, found) + end) + + it('uses last a mark with gt timestamp from instance when reading', + function() + nvim_command('edit /a/b/-') + funcs.setline(1, {'-', '?'}) + wshada('\010\001\017\131\161l\001\161f\196\006/a/b/-\161na') + eq(0, exc_exec(sdrcmd())) + wshada('\010\000\017\131\161l\002\161f\196\006/a/b/-\161na') + eq(0, exc_exec(sdrcmd())) + nvim_command('normal! `a') + eq('-', funcs.getline('.')) + end) + + it('uses last a mark with gt timestamp from file when reading with !', + function() + nvim_command('edit /a/b/-') + funcs.setline(1, {'-', '?'}) + wshada('\010\001\017\131\161l\001\161f\196\006/a/b/-\161na') + eq(0, exc_exec(sdrcmd())) + wshada('\010\000\017\131\161l\002\161f\196\006/a/b/-\161na') + eq(0, exc_exec(sdrcmd(true))) + nvim_command('normal! `a') + eq('?', funcs.getline('.')) + end) + + it('uses last a mark with eq timestamp from instance when reading', + function() + nvim_command('edit /a/b/-') + funcs.setline(1, {'-', '?'}) + wshada('\010\001\017\131\161l\001\161f\196\006/a/b/-\161na') + eq(0, exc_exec(sdrcmd())) + wshada('\010\001\017\131\161l\002\161f\196\006/a/b/-\161na') + eq(0, exc_exec(sdrcmd())) + nvim_command('normal! `a') + eq('-', funcs.getline('.')) + end) + + it('uses last a mark with gt timestamp from file when reading', + function() + nvim_command('edit /a/b/-') + funcs.setline(1, {'-', '?'}) + wshada('\010\001\017\131\161l\001\161f\196\006/a/b/-\161na') + eq(0, exc_exec(sdrcmd())) + wshada('\010\002\017\131\161l\002\161f\196\006/a/b/-\161na') + eq(0, exc_exec(sdrcmd())) + nvim_command('normal! `a') + eq('?', funcs.getline('.')) + end) + + it('uses last a mark with gt timestamp from instance when writing', + function() + nvim_command('edit /a/b/-') + funcs.setline(1, {'-', '?'}) + wshada('\010\001\017\131\161l\001\161f\196\006/a/b/-\161na') + eq(0, exc_exec(sdrcmd())) + wshada('\010\000\017\131\161l\002\161f\196\006/a/b/-\161na') + nvim_command('normal! `a') + eq('-', funcs.getline('.')) + eq(0, exc_exec('wshada ' .. shada_fname)) + local found = 0 + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 10 and v.value.f == '/a/b/-' and v.value.n == ('a'):byte() then + eq(true, v.value.l == 1 or v.value.l == nil) + found = found + 1 + end + end + eq(1, found) + end) + + it('uses last a mark with eq timestamp from instance when writing', + function() + nvim_command('edit /a/b/-') + funcs.setline(1, {'-', '?'}) + wshada('\010\001\017\131\161l\001\161f\196\006/a/b/-\161na') + eq(0, exc_exec(sdrcmd())) + wshada('\010\001\017\131\161l\002\161f\196\006/a/b/-\161na') + nvim_command('normal! `a') + eq('-', funcs.getline('.')) + eq(0, exc_exec('wshada ' .. shada_fname)) + local found = 0 + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 10 and v.value.f == '/a/b/-' and v.value.n == ('a'):byte() then + eq(true, v.value.l == 1 or v.value.l == nil) + found = found + 1 + end + end + eq(1, found) + end) + + it('uses last a mark with gt timestamp from file when writing', + function() + nvim_command('edit /a/b/-') + funcs.setline(1, {'-', '?'}) + wshada('\010\001\017\131\161l\001\161f\196\006/a/b/-\161na') + eq(0, exc_exec(sdrcmd())) + wshada('\010\002\017\131\161l\002\161f\196\006/a/b/-\161na') + nvim_command('normal! `a') + eq('-', funcs.fnamemodify(curbufmeths.get_name(), ':t')) + eq(0, exc_exec('wshada ' .. shada_fname)) + local found = 0 + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 10 and v.value.f == '/a/b/-' and v.value.n == ('a'):byte() then + eq(2, v.value.l) + found = found + 1 + end + end + eq(1, found) + end) +end) + +describe('ShaDa registers support code', function() + before_each(reset) + after_each(function() + clear() + os.remove(shada_fname) + end) + + it('uses last a register with gt timestamp from instance when reading', + function() + wshada('\005\001\015\131\161na\162rX\194\162rc\145\196\001-') + eq(0, exc_exec(sdrcmd())) + wshada('\005\000\015\131\161na\162rX\194\162rc\145\196\001?') + eq(0, exc_exec(sdrcmd())) + eq('-', funcs.getreg('a')) + end) + + it('uses last a register with gt timestamp from file when reading with !', + function() + wshada('\005\001\015\131\161na\162rX\194\162rc\145\196\001-') + eq(0, exc_exec(sdrcmd())) + wshada('\005\000\015\131\161na\162rX\194\162rc\145\196\001?') + eq(0, exc_exec(sdrcmd(true))) + eq('?', funcs.getreg('a')) + end) + + it('uses last a register with eq timestamp from instance when reading', + function() + wshada('\005\001\015\131\161na\162rX\194\162rc\145\196\001-') + eq(0, exc_exec(sdrcmd())) + wshada('\005\001\015\131\161na\162rX\194\162rc\145\196\001?') + eq(0, exc_exec(sdrcmd())) + eq('-', funcs.getreg('a')) + end) + + it('uses last a register with gt timestamp from file when reading', + function() + wshada('\005\001\015\131\161na\162rX\194\162rc\145\196\001-') + eq(0, exc_exec(sdrcmd())) + wshada('\005\002\015\131\161na\162rX\194\162rc\145\196\001?') + eq(0, exc_exec(sdrcmd())) + eq('?', funcs.getreg('a')) + end) + + it('uses last a register with gt timestamp from instance when writing', + function() + wshada('\005\001\015\131\161na\162rX\194\162rc\145\196\001-') + eq(0, exc_exec(sdrcmd())) + wshada('\005\000\015\131\161na\162rX\194\162rc\145\196\001?') + eq('-', funcs.getreg('a')) + eq(0, exc_exec('wshada ' .. shada_fname)) + local found = 0 + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 5 and v.value.n == ('a'):byte() then + eq({'-'}, v.value.rc) + found = found + 1 + end + end + eq(1, found) + end) + + it('uses last a register with eq timestamp from instance when writing', + function() + wshada('\005\001\015\131\161na\162rX\194\162rc\145\196\001-') + eq(0, exc_exec(sdrcmd())) + wshada('\005\001\015\131\161na\162rX\194\162rc\145\196\001?') + eq('-', funcs.getreg('a')) + eq(0, exc_exec('wshada ' .. shada_fname)) + local found = 0 + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 5 and v.value.n == ('a'):byte() then + eq({'-'}, v.value.rc) + found = found + 1 + end + end + eq(1, found) + end) + + it('uses last a register with gt timestamp from file when writing', + function() + wshada('\005\001\015\131\161na\162rX\194\162rc\145\196\001-') + eq(0, exc_exec(sdrcmd())) + wshada('\005\002\015\131\161na\162rX\194\162rc\145\196\001?') + eq('-', funcs.getreg('a')) + eq(0, exc_exec('wshada ' .. shada_fname)) + local found = 0 + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 5 and v.value.n == ('a'):byte() then + eq({'?'}, v.value.rc) + found = found + 1 + end + end + eq(1, found) + end) +end) + +describe('ShaDa jumps support code', function() + before_each(reset) + after_each(function() + clear() + os.remove(shada_fname) + end) + + it('merges jumps when reading', function() + wshada('\008\001\018\131\162mX\195\161f\196\006/a/b/c\161l\002' + .. '\008\004\018\131\162mX\195\161f\196\006/a/b/d\161l\002' + .. '\008\007\018\131\162mX\195\161f\196\006/a/b/e\161l\002') + eq(0, exc_exec(sdrcmd())) + wshada('\008\001\018\131\162mX\195\161f\196\006/a/b/c\161l\002' + .. '\008\004\018\131\162mX\195\161f\196\006/a/b/d\161l\003' + .. '\008\007\018\131\162mX\195\161f\196\006/a/b/f\161l\002') + eq(0, exc_exec(sdrcmd())) + eq('', curbufmeths.get_name()) + eq('\n' + .. ' jump line col file/text\n' + .. ' 6 2 0 /a/b/c\n' + .. ' 5 2 0 /a/b/d\n' + .. ' 4 3 0 /a/b/d\n' + .. ' 3 2 0 /a/b/e\n' + .. ' 2 2 0 /a/b/f\n' + .. ' 1 1 0 \n' + .. '>', redir_exec('jumps')) + end) + + it('merges jumps when writing', function() + wshada('\008\001\018\131\162mX\195\161f\196\006/a/b/c\161l\002' + .. '\008\004\018\131\162mX\195\161f\196\006/a/b/d\161l\002' + .. '\008\007\018\131\162mX\195\161f\196\006/a/b/e\161l\002') + eq(0, exc_exec(sdrcmd())) + wshada('\008\001\018\131\162mX\195\161f\196\006/a/b/c\161l\002' + .. '\008\004\018\131\162mX\195\161f\196\006/a/b/d\161l\003' + .. '\008\007\018\131\162mX\195\161f\196\006/a/b/f\161l\002') + eq(0, exc_exec('wshada ' .. shada_fname)) + local jumps = { + {file='/a/b/c', line=2}, + {file='/a/b/d', line=2}, + {file='/a/b/d', line=3}, + {file='/a/b/e', line=2}, + {file='/a/b/f', line=2}, + } + local found = 0 + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 8 then + found = found + 1 + eq(jumps[found].file, v.value.f) + eq(jumps[found].line, v.value.l) + end + end + eq(found, #jumps) + end) + + it('merges JUMPLISTSIZE jumps when writing', function() + local jumps = {} + local shada = '' + for i = 1,100 do + shada = shada .. ('\008%c\018\131\162mX\195\161f\196\006/a/b/c\161l%c' + ):format(i, i) + jumps[i] = {file='/a/b/c', line=i} + end + wshada(shada) + eq(0, exc_exec(sdrcmd())) + local shada = '' + for i = 1,101 do + local t = i * 2 + shada = shada .. ( + '\008\204%c\019\131\162mX\195\161f\196\006/a/b/c\161l\204%c' + ):format(t, t) + jumps[(t > #jumps + 1) and (#jumps + 1) or t] = {file='/a/b/c', line=t} + end + wshada(shada) + eq(0, exc_exec('wshada ' .. shada_fname)) + local shift = #jumps - 100 + for i = 1,100 do + jumps[i] = jumps[i + shift] + end + local found = 0 + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 8 then + found = found + 1 + eq(jumps[found].file, v.value.f) + eq(jumps[found].line, v.value.l) + end + end + eq(found, 100) + end) +end) + +describe('ShaDa changes support code', function() + before_each(reset) + after_each(function() + clear() + os.remove(shada_fname) + end) + + it('merges changes when reading', function() + nvim_command('edit /a/b/c') + nvim_command('keepjumps call setline(1, range(7))') + wshada('\011\001\018\131\162mX\195\161f\196\006/a/b/c\161l\001' + .. '\011\004\018\131\162mX\195\161f\196\006/a/b/c\161l\002' + .. '\011\007\018\131\162mX\195\161f\196\006/a/b/c\161l\003') + eq(0, exc_exec(sdrcmd())) + wshada('\011\001\018\131\162mX\194\161f\196\006/a/b/c\161l\001' + .. '\011\004\018\131\162mX\195\161f\196\006/a/b/c\161l\005' + .. '\011\008\018\131\162mX\195\161f\196\006/a/b/c\161l\004') + eq(0, exc_exec(sdrcmd())) + eq('\n' + .. 'change line col text\n' + .. ' 5 1 0 0\n' + .. ' 4 2 0 1\n' + .. ' 3 5 0 4\n' + .. ' 2 3 0 2\n' + .. ' 1 4 0 3\n' + .. '>', redir_exec('changes')) + end) + + it('merges changes when writing', function() + nvim_command('edit /a/b/c') + nvim_command('keepjumps call setline(1, range(7))') + wshada('\011\001\018\131\162mX\195\161f\196\006/a/b/c\161l\001' + .. '\011\004\018\131\162mX\195\161f\196\006/a/b/c\161l\002' + .. '\011\007\018\131\162mX\195\161f\196\006/a/b/c\161l\003') + eq(0, exc_exec(sdrcmd())) + wshada('\011\001\018\131\162mX\194\161f\196\006/a/b/c\161l\001' + .. '\011\004\018\131\162mX\195\161f\196\006/a/b/c\161l\005' + .. '\011\008\018\131\162mX\195\161f\196\006/a/b/c\161l\004') + eq(0, exc_exec('wshada ' .. shada_fname)) + local changes = { + {line=1}, + {line=2}, + {line=5}, + {line=3}, + {line=4}, + } + local found = 0 + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 11 and v.value.f == '/a/b/c' then + found = found + 1 + eq(changes[found].line, v.value.l or 1) + end + end + eq(found, #changes) + end) + + it('merges JUMPLISTSIZE changes when writing', function() + nvim_command('edit /a/b/c') + nvim_command('keepjumps call setline(1, range(202))') + local changes = {} + local shada = '' + for i = 1,100 do + shada = shada .. ('\011%c\018\131\162mX\195\161f\196\006/a/b/c\161l%c' + ):format(i, i) + changes[i] = {line=i} + end + wshada(shada) + eq(0, exc_exec(sdrcmd())) + local shada = '' + for i = 1,101 do + local t = i * 2 + shada = shada .. ( + '\011\204%c\019\131\162mX\195\161f\196\006/a/b/c\161l\204%c' + ):format(t, t) + changes[(t > #changes + 1) and (#changes + 1) or t] = {line=t} + end + wshada(shada) + eq(0, exc_exec('wshada ' .. shada_fname)) + local shift = #changes - 100 + for i = 1,100 do + changes[i] = changes[i + shift] + end + local found = 0 + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 11 and v.value.f == '/a/b/c' then + found = found + 1 + eq(changes[found].line, v.value.l) + end + end + eq(found, 100) + end) + + it('merges JUMPLISTSIZE changes when writing, with new items between old', + function() + nvim_command('edit /a/b/c') + nvim_command('keepjumps call setline(1, range(202))') + local shada = '' + for i = 1,101 do + local t = i * 2 + shada = shada .. ( + '\011\204%c\019\131\162mX\195\161f\196\006/a/b/c\161l\204%c' + ):format(t, t) + end + wshada(shada) + eq(0, exc_exec(sdrcmd())) + local shada = '' + for i = 1,100 do + shada = shada .. ('\011%c\018\131\162mX\195\161f\196\006/a/b/c\161l%c' + ):format(i, i) + end + local changes = {} + for i = 1, 100 do + changes[i] = {line=i} + end + for i = 1, 101 do + local t = i * 2 + changes[(t > #changes + 1) and (#changes + 1) or t] = {line=t} + end + wshada(shada) + eq(0, exc_exec('wshada ' .. shada_fname)) + local shift = #changes - 100 + for i = 1,100 do + changes[i] = changes[i + shift] + end + local found = 0 + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 11 and v.value.f == '/a/b/c' then + found = found + 1 + eq(changes[found].line, v.value.l) + end + end + eq(found, 100) + end) +end) diff --git a/test/functional/shada/registers_spec.lua b/test/functional/shada/registers_spec.lua new file mode 100644 index 0000000000..f0133b1086 --- /dev/null +++ b/test/functional/shada/registers_spec.lua @@ -0,0 +1,163 @@ +-- ShaDa registers saving/reading support +local helpers = require('test.functional.helpers') +local nvim_command, funcs, eq = helpers.command, helpers.funcs, helpers.eq + +local shada_helpers = require('test.functional.shada.helpers') +local reset, set_additional_cmd, clear = + shada_helpers.reset, shada_helpers.set_additional_cmd, + shada_helpers.clear + +local setreg = function(name, contents, typ) + if type(contents) == 'string' then + contents = {contents} + end + funcs.setreg(name, contents, typ) +end + +local getreg = function(name) + return { + funcs.getreg(name, 1, 1), + funcs.getregtype(name), + } +end + +describe('ShaDa support code', function() + before_each(reset) + after_each(clear) + + it('is able to dump and restore registers and their type', function() + setreg('c', {'d', 'e', ''}, 'c') + setreg('l', {'a', 'b', 'cde'}, 'l') + setreg('b', {'bca', 'abc', 'cba'}, 'b3') + nvim_command('qall') + reset() + eq({{'d', 'e', ''}, 'v'}, getreg('c')) + eq({{'a', 'b', 'cde'}, 'V'}, getreg('l')) + eq({{'bca', 'abc', 'cba'}, '\0223'}, getreg('b')) + end) + + it('does not dump registers with zero <', function() + nvim_command('set shada=\'0,<0') + setreg('c', {'d', 'e', ''}, 'c') + setreg('l', {'a', 'b', 'cde'}, 'l') + setreg('b', {'bca', 'abc', 'cba'}, 'b3') + nvim_command('qall') + reset() + eq({nil, ''}, getreg('c')) + eq({nil, ''}, getreg('l')) + eq({nil, ''}, getreg('b')) + end) + + it('does restore registers with zero <', function() + setreg('c', {'d', 'e', ''}, 'c') + setreg('l', {'a', 'b', 'cde'}, 'l') + setreg('b', {'bca', 'abc', 'cba'}, 'b3') + set_additional_cmd('set shada=\'0,<0') + nvim_command('qall') + reset() + eq({{'d', 'e', ''}, 'v'}, getreg('c')) + eq({{'a', 'b', 'cde'}, 'V'}, getreg('l')) + eq({{'bca', 'abc', 'cba'}, '\0223'}, getreg('b')) + end) + + it('does not dump registers with zero "', function() + nvim_command('set shada=\'0,\\"0') + setreg('c', {'d', 'e', ''}, 'c') + setreg('l', {'a', 'b', 'cde'}, 'l') + setreg('b', {'bca', 'abc', 'cba'}, 'b3') + nvim_command('qall') + reset() + eq({nil, ''}, getreg('c')) + eq({nil, ''}, getreg('l')) + eq({nil, ''}, getreg('b')) + end) + + it('does restore registers with zero "', function() + setreg('c', {'d', 'e', ''}, 'c') + setreg('l', {'a', 'b', 'cde'}, 'l') + setreg('b', {'bca', 'abc', 'cba'}, 'b3') + set_additional_cmd('set shada=\'0,\\"0') + nvim_command('qall') + reset() + eq({{'d', 'e', ''}, 'v'}, getreg('c')) + eq({{'a', 'b', 'cde'}, 'V'}, getreg('l')) + eq({{'bca', 'abc', 'cba'}, '\0223'}, getreg('b')) + end) + + it('does dump registers with zero ", but non-zero <', function() + nvim_command('set shada=\'0,\\"0,<50') + setreg('c', {'d', 'e', ''}, 'c') + setreg('l', {'a', 'b', 'cde'}, 'l') + setreg('b', {'bca', 'abc', 'cba'}, 'b3') + nvim_command('qall') + reset() + eq({{'d', 'e', ''}, 'v'}, getreg('c')) + eq({{'a', 'b', 'cde'}, 'V'}, getreg('l')) + eq({{'bca', 'abc', 'cba'}, '\0223'}, getreg('b')) + end) + + it('does limit number of lines according to <', function() + nvim_command('set shada=\'0,<2') + setreg('o', {'d'}, 'c') + setreg('t', {'a', 'b', 'cde'}, 'l') + nvim_command('qall') + reset() + eq({{'d'}, 'v'}, getreg('o')) + eq({nil, ''}, getreg('t')) + end) + + it('does limit number of lines according to "', function() + nvim_command('set shada=\'0,\\"2') + setreg('o', {'d'}, 'c') + setreg('t', {'a', 'b', 'cde'}, 'l') + nvim_command('qall') + reset() + eq({{'d'}, 'v'}, getreg('o')) + eq({nil, ''}, getreg('t')) + end) + + it('does limit number of lines according to < rather then "', function() + nvim_command('set shada=\'0,\\"2,<3') + setreg('o', {'d'}, 'c') + setreg('t', {'a', 'b', 'cde'}, 'l') + setreg('h', {'abc', 'acb', 'bac', 'bca', 'cab', 'cba'}, 'b3') + nvim_command('qall') + reset() + eq({{'d'}, 'v'}, getreg('o')) + eq({{'a', 'b', 'cde'}, 'V'}, getreg('t')) + eq({nil, ''}, getreg('h')) + end) + + it('dumps and loads register correctly when &encoding is not UTF-8', + function() + set_additional_cmd('set encoding=latin1') + reset() + -- \171 is U+00AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK in latin1 + setreg('e', {'\171'}, 'c') + nvim_command('qall') + reset() + eq({{'\171'}, 'v'}, getreg('e')) + end) + + it('dumps and loads history correctly when &encoding /= UTF-8 when dumping', + function() + set_additional_cmd('set encoding=latin1') + reset() + -- \171 is U+00AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK in latin1 + setreg('e', {'\171'}, 'c') + set_additional_cmd('') + nvim_command('qall') + reset() + eq({{'«'}, 'v'}, getreg('e')) + end) + + it('dumps and loads history correctly when &encoding /= UTF-8 when loading', + function() + -- \171 is U+00AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK in latin1 + setreg('e', {'«'}, 'c') + set_additional_cmd('set encoding=latin1') + nvim_command('qall') + reset() + eq({{'\171'}, 'v'}, getreg('e')) + end) +end) diff --git a/test/functional/shada/shada_spec.lua b/test/functional/shada/shada_spec.lua new file mode 100644 index 0000000000..2bc855a239 --- /dev/null +++ b/test/functional/shada/shada_spec.lua @@ -0,0 +1,232 @@ +-- Other ShaDa tests +local helpers = require('test.functional.helpers') +local meths, nvim_command, funcs, eq = + helpers.meths, helpers.command, helpers.funcs, helpers.eq +local write_file, spawn, set_session, nvim_prog, exc_exec = + helpers.write_file, helpers.spawn, helpers.set_session, helpers.nvim_prog, + helpers.exc_exec +local lfs = require('lfs') +local paths = require('test.config.paths') + +local msgpack = require('MessagePack') + +local shada_helpers = require('test.functional.shada.helpers') +local reset, clear, get_shada_rw = + shada_helpers.reset, shada_helpers.clear, shada_helpers.get_shada_rw +local read_shada_file = shada_helpers.read_shada_file + +local wshada, _, shada_fname, clean = + get_shada_rw('Xtest-functional-shada-shada.shada') + +describe('ShaDa support code', function() + before_each(reset) + after_each(function() + clear() + clean() + end) + + it('preserves `s` item size limit with unknown entries', function() + wshada('\100\000\207\000\000\000\000\000\000\004\000\218\003\253' .. ('-'):rep(1024 - 3) + .. '\100\000\207\000\000\000\000\000\000\004\001\218\003\254' .. ('-'):rep(1025 - 3)) + eq(0, exc_exec('wshada ' .. shada_fname)) + local found = 0 + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 100 then + found = found + 1 + end + end + eq(2, found) + eq(0, exc_exec('set shada-=s10 shada+=s1')) + eq(0, exc_exec('wshada ' .. shada_fname)) + found = 0 + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 100 then + found = found + 1 + end + end + eq(1, found) + end) + + it('preserves `s` item size limit with instance history entries', function() + local hist1 = ('-'):rep(1024 - 5) + local hist2 = ('-'):rep(1025 - 5) + nvim_command('set shada-=s10 shada+=s1') + funcs.histadd(':', hist1) + funcs.histadd(':', hist2) + eq(0, exc_exec('wshada ' .. shada_fname)) + local found = 0 + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 4 then + found = found + 1 + eq(hist1, v.value[2]) + end + end + eq(1, found) + end) + + it('leaves .tmp.a in-place when there is error in original ShaDa', function() + wshada('Some text file') + eq('Vim(wshada):E576: Error while reading ShaDa file: last entry specified that it occupies 109 bytes, but file ended earlier', exc_exec('wshada ' .. shada_fname)) + eq(1, read_shada_file(shada_fname .. '.tmp.a')[1].type) + end) + + it('does not leave .tmp.a in-place when there is error in original ShaDa, but writing with bang', function() + wshada('Some text file') + eq(0, exc_exec('wshada! ' .. shada_fname)) + eq(1, read_shada_file(shada_fname)[1].type) + eq(nil, lfs.attributes(shada_fname .. '.tmp.a')) + end) + + it('leaves .tmp.b in-place when there is error in original ShaDa and it has .tmp.a', function() + wshada('Some text file') + eq('Vim(wshada):E576: Error while reading ShaDa file: last entry specified that it occupies 109 bytes, but file ended earlier', exc_exec('wshada ' .. shada_fname)) + eq('Vim(wshada):E576: Error while reading ShaDa file: last entry specified that it occupies 109 bytes, but file ended earlier', exc_exec('wshada ' .. shada_fname)) + eq(1, read_shada_file(shada_fname .. '.tmp.a')[1].type) + eq(1, read_shada_file(shada_fname .. '.tmp.b')[1].type) + end) + + it('leaves .tmp.z in-place when there is error in original ShaDa and it has .tmp.a … .tmp.x', function() + wshada('Some text file') + local i = ('a'):byte() + while i < ('z'):byte() do + write_file(shada_fname .. ('.tmp.%c'):format(i), 'Some text file', true) + i = i + 1 + end + eq('Vim(wshada):E576: Error while reading ShaDa file: last entry specified that it occupies 109 bytes, but file ended earlier', exc_exec('wshada ' .. shada_fname)) + eq(1, read_shada_file(shada_fname .. '.tmp.z')[1].type) + end) + + it('errors out when there are .tmp.a … .tmp.z ShaDa files', function() + wshada('') + local i = ('a'):byte() + while i <= ('z'):byte() do + write_file(shada_fname .. ('.tmp.%c'):format(i), '', true) + i = i + 1 + end + eq('Vim(wshada):E138: All Xtest-functional-shada-shada.shada.tmp.X files exist, cannot write ShaDa file!', exc_exec('wshada ' .. shada_fname)) + end) + + it('reads correctly various timestamps', function() + local mpack = { + '\100', -- Positive fixnum 100 + '\204\255', -- uint 8 255 + '\205\010\003', -- uint 16 2563 + '\206\255\010\030\004', -- uint 32 4278853124 + '\207\005\100\060\250\255\010\030\004', -- uint 64 388502516579048964 + } + local s = '\100' + local e = '\001\192' + wshada(s .. table.concat(mpack, e .. s) .. e) + eq(0, exc_exec('wshada ' .. shada_fname)) + local found = 0 + local typ = select(2, msgpack.unpacker(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) + end + end + eq(#mpack, 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) + eq(nil, lfs.attributes('NONE')) + eq(nil, lfs.attributes('NONE.tmp.a')) + end) + + it('does not read NONE file', function() + write_file('NONE', '\005\001\015\131\161na\162rX\194\162rc\145\196\001-') + local session = spawn({nvim_prog, '-u', 'NONE', '-i', 'NONE', '--embed'}, + true) + set_session(session) + eq('', funcs.getreg('a')) + session:exit(0) + os.remove('NONE') + end) + + local marklike = {[7]=true, [8]=true, [10]=true, [11]=true} + local find_file = function(fname) + local found = {} + for _, v in ipairs(read_shada_file(shada_fname)) do + if marklike[v.type] and v.value.f == fname then + found[v.type] = (found[v.type] or 0) + 1 + elseif v.type == 9 then + for _, b in ipairs(v.value) do + if b.f == fname then + found[v.type] = (found[v.type] or 0) + 1 + end + end + end + end + return found + end + + it('correctly uses shada-r option', function() + meths.set_var('__home', paths.test_source_path) + nvim_command('let $HOME = __home') + nvim_command('unlet __home') + nvim_command('edit ~/README.md') + nvim_command('normal! GmAggmaAabc') + nvim_command('undo') + nvim_command('set shada+=%') + nvim_command('wshada! ' .. shada_fname) + local readme_fname = paths.test_source_path .. '/README.md' + eq({[7]=1, [8]=2, [9]=1, [10]=4, [11]=1}, find_file(readme_fname)) + nvim_command('set shada+=r~') + nvim_command('wshada! ' .. shada_fname) + eq({}, find_file(readme_fname)) + nvim_command('set shada-=r~') + nvim_command('wshada! ' .. shada_fname) + eq({[7]=1, [8]=2, [9]=1, [10]=4, [11]=1}, find_file(readme_fname)) + nvim_command('set shada+=r' .. paths.test_source_path) + nvim_command('wshada! ' .. shada_fname) + eq({}, find_file(readme_fname)) + end) + + it('correctly ignores case with shada-r option', function() + local pwd = funcs.getcwd() + local relfname = 'абв/test' + local fname = pwd .. '/' .. relfname + meths.set_var('__fname', fname) + nvim_command('silent! edit `=__fname`') + funcs.setline(1, {'a', 'b', 'c', 'd'}) + nvim_command('normal! GmAggmaAabc') + nvim_command('undo') + nvim_command('set shada+=%') + nvim_command('wshada! ' .. shada_fname) + eq({[7]=1, [8]=2, [9]=1, [10]=4, [11]=2}, find_file(fname)) + nvim_command('set shada+=r' .. pwd .. '/АБВ') + nvim_command('wshada! ' .. shada_fname) + eq({}, find_file(fname)) + end) + + it('is able to set &shada after &viminfo', function() + meths.set_option('viminfo', '\'10') + eq('\'10', meths.get_option('viminfo')) + eq('\'10', meths.get_option('shada')) + meths.set_option('shada', '') + eq('', meths.get_option('viminfo')) + eq('', meths.get_option('shada')) + end) + + it('is able to set all& after setting &shada', function() + meths.set_option('shada', '\'10') + eq('\'10', meths.get_option('viminfo')) + eq('\'10', meths.get_option('shada')) + nvim_command('set all&') + eq('!,\'100,<50,s10,h', meths.get_option('viminfo')) + eq('!,\'100,<50,s10,h', meths.get_option('shada')) + end) + + it('is able to set &shada after &viminfo using :set', function() + nvim_command('set viminfo=\'10') + eq('\'10', meths.get_option('viminfo')) + eq('\'10', meths.get_option('shada')) + nvim_command('set shada=') + eq('', meths.get_option('viminfo')) + eq('', meths.get_option('shada')) + end) +end) diff --git a/test/functional/shada/variables_spec.lua b/test/functional/shada/variables_spec.lua new file mode 100644 index 0000000000..6225971e5f --- /dev/null +++ b/test/functional/shada/variables_spec.lua @@ -0,0 +1,139 @@ +-- ShaDa variables saving/reading support +local helpers = require('test.functional.helpers') +local meths, funcs, nvim_command, eq = + helpers.meths, helpers.funcs, helpers.command, helpers.eq + +local shada_helpers = require('test.functional.shada.helpers') +local reset, set_additional_cmd, clear = + shada_helpers.reset, shada_helpers.set_additional_cmd, + shada_helpers.clear + +describe('ShaDa support code', function() + before_each(reset) + after_each(clear) + + it('is able to dump and read back string variable', function() + meths.set_var('STRVAR', 'foo') + nvim_command('set shada+=!') + nvim_command('wshada') + reset() + nvim_command('set shada+=!') + nvim_command('rshada') + eq('foo', meths.get_var('STRVAR')) + end) + + local autotest = function(tname, varname, varval) + it('is able to dump and read back ' .. tname .. ' variable automatically', + function() + set_additional_cmd('set shada+=!') + reset() + meths.set_var(varname, varval) + -- Exit during `reset` is not a regular exit: it does not write shada + -- automatically + nvim_command('qall') + reset() + eq(varval, meths.get_var(varname)) + end) + end + + autotest('string', 'STRVAR', 'foo') + autotest('number', 'NUMVAR', 42) + autotest('float', 'FLTVAR', 42.5) + autotest('dictionary', 'DCTVAR', {a=10}) + autotest('list', 'LSTVAR', {{a=10}, {b=10.5}, {c='str'}}) + + it('does not read back variables without `!` in &shada', function() + meths.set_var('STRVAR', 'foo') + nvim_command('set shada+=!') + nvim_command('wshada') + set_additional_cmd('set shada-=!') + reset() + nvim_command('rshada') + eq(0, funcs.exists('g:STRVAR')) + end) + + it('does not dump variables without `!` in &shada', function() + nvim_command('set shada-=!') + meths.set_var('STRVAR', 'foo') + nvim_command('wshada') + reset() + nvim_command('set shada+=!') + nvim_command('rshada') + eq(0, funcs.exists('g:STRVAR')) + end) + + it('does not dump session variables', function() + nvim_command('set shada+=!') + meths.set_var('StrVar', 'foo') + nvim_command('wshada') + reset() + nvim_command('set shada+=!') + nvim_command('rshada') + eq(0, funcs.exists('g:StrVar')) + end) + + it('does not dump regular variables', function() + nvim_command('set shada+=!') + meths.set_var('str_var', 'foo') + nvim_command('wshada') + reset() + nvim_command('set shada+=!') + nvim_command('rshada') + eq(0, funcs.exists('g:str_var')) + end) + + it('dumps and loads variables correctly when &encoding is not UTF-8', + function() + set_additional_cmd('set encoding=latin1') + reset() + -- \171 is U+00AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK in latin1 + meths.set_var('STRVAR', '\171') + meths.set_var('LSTVAR', {'\171'}) + meths.set_var('DCTVAR', {['\171']='\171'}) + meths.set_var('NESTEDVAR', {['\171']={{'\171'}, {['\171']='\171'}, + {a='Test'}}}) + nvim_command('qall') + reset() + eq('\171', meths.get_var('STRVAR')) + eq({'\171'}, meths.get_var('LSTVAR')) + eq({['\171']='\171'}, meths.get_var('DCTVAR')) + eq({['\171']={{'\171'}, {['\171']='\171'}, {a='Test'}}}, + meths.get_var('NESTEDVAR')) + end) + + it('dumps and loads variables correctly when &encoding /= UTF-8 when dumping', + function() + set_additional_cmd('set encoding=latin1') + reset() + -- \171 is U+00AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK in latin1 + meths.set_var('STRVAR', '\171') + meths.set_var('LSTVAR', {'\171'}) + meths.set_var('DCTVAR', {['\171']='\171'}) + meths.set_var('NESTEDVAR', {['\171']={{'\171'}, {['\171']='\171'}, + {a='Test'}}}) + set_additional_cmd('') + nvim_command('qall') + reset() + eq('«', meths.get_var('STRVAR')) + eq({'«'}, meths.get_var('LSTVAR')) + eq({['«']='«'}, meths.get_var('DCTVAR')) + eq({['«']={{'«'}, {['«']='«'}, {a='Test'}}}, meths.get_var('NESTEDVAR')) + end) + + it('dumps and loads variables correctly when &encoding /= UTF-8 when loading', + function() + -- \171 is U+00AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK in latin1 + meths.set_var('STRVAR', '«') + meths.set_var('LSTVAR', {'«'}) + meths.set_var('DCTVAR', {['«']='«'}) + meths.set_var('NESTEDVAR', {['«']={{'«'}, {['«']='«'}, {a='Test'}}}) + set_additional_cmd('set encoding=latin1') + nvim_command('qall') + reset() + eq('\171', meths.get_var('STRVAR')) + eq({'\171'}, meths.get_var('LSTVAR')) + eq({['\171']='\171'}, meths.get_var('DCTVAR')) + eq({['\171']={{'\171'}, {['\171']='\171'}, {a='Test'}}}, + meths.get_var('NESTEDVAR')) + end) +end) |