aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/config/paths.lua.in1
-rw-r--r--test/functional/ex_cmds/recover_spec.lua3
-rw-r--r--test/functional/ex_cmds/wviminfo_spec.lua37
-rw-r--r--test/functional/ex_getln/history_spec.lua36
-rw-r--r--test/functional/helpers.lua77
-rw-r--r--test/functional/legacy/074_global_var_in_viminfo_spec.lua24
-rw-r--r--test/functional/shada/buffers_spec.lua51
-rw-r--r--test/functional/shada/compatibility_spec.lua453
-rw-r--r--test/functional/shada/errors_spec.lua490
-rw-r--r--test/functional/shada/helpers.lua94
-rw-r--r--test/functional/shada/history_spec.lua312
-rw-r--r--test/functional/shada/marks_spec.lua180
-rw-r--r--test/functional/shada/merging_spec.lua1032
-rw-r--r--test/functional/shada/registers_spec.lua163
-rw-r--r--test/functional/shada/shada_spec.lua232
-rw-r--r--test/functional/shada/variables_spec.lua139
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)