diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/functional/plugin/helpers.lua | 41 | ||||
-rw-r--r-- | test/functional/plugin/msgpack_spec.lua | 699 | ||||
-rw-r--r-- | test/functional/plugin/shada_spec.lua | 2833 |
3 files changed, 3573 insertions, 0 deletions
diff --git a/test/functional/plugin/helpers.lua b/test/functional/plugin/helpers.lua new file mode 100644 index 0000000000..9762ca314e --- /dev/null +++ b/test/functional/plugin/helpers.lua @@ -0,0 +1,41 @@ +local paths = require('test.config.paths') + +local helpers = require('test.functional.helpers') +local spawn, set_session, nvim_prog, merge_args = + helpers.spawn, helpers.set_session, helpers.nvim_prog, helpers.merge_args + +local additional_cmd = '' + +local function nvim_argv(shada_file) + local rtp_value = ('\'%s/runtime\''):format( + paths.test_source_path:gsub('\'', '\'\'')) + local nvim_argv = {nvim_prog, '-u', 'NORC', '-i', shada_file or 'NONE', '-N', + '--cmd', 'set shortmess+=I background=light noswapfile', + '--cmd', 'let &runtimepath=' .. rtp_value, + '--cmd', additional_cmd, + '--embed'} + if helpers.prepend_argv then + return merge_args(helpers.prepend_argv, nvim_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) +end + +local set_additional_cmd = function(s) + additional_cmd = s +end + +return { + reset=reset, + set_additional_cmd=set_additional_cmd, +} diff --git a/test/functional/plugin/msgpack_spec.lua b/test/functional/plugin/msgpack_spec.lua new file mode 100644 index 0000000000..18ff0f5156 --- /dev/null +++ b/test/functional/plugin/msgpack_spec.lua @@ -0,0 +1,699 @@ +local helpers = require('test.functional.helpers') +local eq, nvim_eval, nvim_command, exc_exec = + helpers.eq, helpers.eval, helpers.command, helpers.exc_exec + +local plugin_helpers = require('test.functional.plugin.helpers') +local reset = plugin_helpers.reset + +describe('In autoload/msgpack.vim', function() + before_each(reset) + + local sp = function(typ, val) + return ('{"_TYPE": v:msgpack_types.%s, "_VAL": %s}'):format(typ, val) + end + local mapsp = function(...) + local val = '' + for i=1,(select('#', ...)/2) do + val = ('%s[%s,%s],'):format(val, select(i * 2 - 1, ...), + select(i * 2, ...)) + end + return sp('map', '[' .. val .. ']') + end + + local nan = -(1.0/0.0-1.0/0.0) + local minus_nan = 1.0/0.0-1.0/0.0 + local inf = 1.0/0.0 + local minus_inf = -(1.0/0.0) + local has_minus_nan = tostring(nan) ~= tostring(minus_nan) + + describe('function msgpack#equal', function() + local msgpack_eq = function(expected, a, b) + eq(expected, nvim_eval(('msgpack#equal(%s, %s)'):format(a, b))) + if a ~= b then + eq(expected, nvim_eval(('msgpack#equal(%s, %s)'):format(b, a))) + end + end + it('compares raw integers correctly', function() + msgpack_eq(1, '1', '1') + msgpack_eq(0, '1', '0') + end) + it('compares integer specials correctly', function() + msgpack_eq(1, sp('integer', '[-1, 1, 0, 0]'), + sp('integer', '[-1, 1, 0, 0]')) + msgpack_eq(0, sp('integer', '[-1, 1, 0, 0]'), + sp('integer', '[ 1, 1, 0, 0]')) + end) + it('compares integer specials with raw integer correctly', function() + msgpack_eq(1, sp('integer', '[-1, 0, 0, 1]'), '-1') + msgpack_eq(0, sp('integer', '[-1, 0, 0, 1]'), '1') + msgpack_eq(0, sp('integer', '[ 1, 0, 0, 1]'), '-1') + msgpack_eq(1, sp('integer', '[ 1, 0, 0, 1]'), '1') + end) + it('compares integer with float correctly', function() + msgpack_eq(0, '0', '0.0') + end) + it('compares raw binaries correctly', function() + msgpack_eq(1, '"abc\\ndef"', '"abc\\ndef"') + msgpack_eq(0, '"abc\\ndef"', '"abc\\nghi"') + end) + it('compares binary specials correctly', function() + msgpack_eq(1, sp('binary', '["abc\\n", "def"]'), + sp('binary', '["abc\\n", "def"]')) + msgpack_eq(0, sp('binary', '["abc", "def"]'), + sp('binary', '["abc\\n", "def"]')) + end) + it('compares binary specials with raw binaries correctly', function() + msgpack_eq(1, sp('binary', '["abc", "def"]'), '"abc\\ndef"') + msgpack_eq(0, sp('binary', '["abc", "def"]'), '"abcdef"') + end) + it('compares string specials correctly', function() + msgpack_eq(1, sp('string', '["abc\\n", "def"]'), + sp('string', '["abc\\n", "def"]')) + msgpack_eq(0, sp('string', '["abc", "def"]'), + sp('string', '["abc\\n", "def"]')) + end) + it('compares string specials with binary correctly', function() + msgpack_eq(0, sp('string', '["abc\\n", "def"]'), + sp('binary', '["abc\\n", "def"]')) + msgpack_eq(0, sp('string', '["abc", "def"]'), '"abc\\ndef"') + msgpack_eq(0, sp('binary', '["abc\\n", "def"]'), + sp('string', '["abc\\n", "def"]')) + msgpack_eq(0, '"abc\\ndef"', sp('string', '["abc", "def"]')) + end) + it('compares ext specials correctly', function() + msgpack_eq(1, sp('ext', '[1, ["", "ac"]]'), sp('ext', '[1, ["", "ac"]]')) + msgpack_eq(0, sp('ext', '[2, ["", "ac"]]'), sp('ext', '[1, ["", "ac"]]')) + msgpack_eq(0, sp('ext', '[1, ["", "ac"]]'), sp('ext', '[1, ["", "abc"]]')) + end) + it('compares raw maps correctly', function() + msgpack_eq(1, '{"a": 1, "b": 2}', '{"b": 2, "a": 1}') + msgpack_eq(1, '{}', '{}') + msgpack_eq(0, '{}', '{"a": 1}') + msgpack_eq(0, '{"a": 2}', '{"a": 1}') + msgpack_eq(0, '{"a": 1}', '{"b": 1}') + msgpack_eq(0, '{"a": 1}', '{"a": 1, "b": 1}') + msgpack_eq(0, '{"a": 1, "b": 1}', '{"b": 1}') + end) + it('compares map specials correctly', function() + msgpack_eq(1, mapsp(), mapsp()) + msgpack_eq(1, mapsp(sp('binary', '[""]'), '""'), + mapsp(sp('binary', '[""]'), '""')) + msgpack_eq(1, mapsp(mapsp('1', '1'), mapsp('1', '1')), + mapsp(mapsp('1', '1'), mapsp('1', '1'))) + msgpack_eq(0, mapsp(), mapsp('1', '1')) + msgpack_eq(0, mapsp(sp('binary', '["a"]'), '""'), + mapsp(sp('binary', '[""]'), '""')) + msgpack_eq(0, mapsp(sp('binary', '[""]'), '"a"'), + mapsp(sp('binary', '[""]'), '""')) + msgpack_eq(0, mapsp(sp('binary', '["a"]'), '"a"'), + mapsp(sp('binary', '[""]'), '""')) + msgpack_eq(0, mapsp(mapsp('1', '1'), mapsp('1', '1')), + mapsp(sp('binary', '[""]'), mapsp('1', '1'))) + msgpack_eq(0, mapsp(mapsp('1', '1'), mapsp('1', '1')), + mapsp(mapsp('2', '1'), mapsp('1', '1'))) + msgpack_eq(0, mapsp(mapsp('1', '1'), mapsp('1', '1')), + mapsp(mapsp('1', '2'), mapsp('1', '1'))) + msgpack_eq(0, mapsp(mapsp('1', '1'), mapsp('1', '1')), + mapsp(mapsp('1', '1'), mapsp('2', '1'))) + msgpack_eq(0, mapsp(mapsp('1', '1'), mapsp('1', '1')), + mapsp(mapsp('1', '1'), mapsp('1', '2'))) + msgpack_eq(1, mapsp(mapsp('2', '1'), mapsp('1', '1'), + mapsp('1', '1'), mapsp('1', '1')), + mapsp(mapsp('1', '1'), mapsp('1', '1'), + mapsp('2', '1'), mapsp('1', '1'))) + end) + it('compares map specials with raw maps correctly', function() + msgpack_eq(1, mapsp(), '{}') + msgpack_eq(1, mapsp(sp('string', '["1"]'), '1'), '{"1": 1}') + msgpack_eq(1, mapsp(sp('string', '["1"]'), sp('integer', '[1, 0, 0, 1]')), + '{"1": 1}') + msgpack_eq(0, mapsp(sp('integer', '[1, 0, 0, 1]'), sp('string', '["1"]')), + '{1: "1"}') + msgpack_eq(0, mapsp('"1"', sp('integer', '[1, 0, 0, 1]')), + '{"1": 1}') + msgpack_eq(0, + mapsp(sp('string', '["1"]'), '1', sp('string', '["2"]'), '2'), + '{"1": 1}') + msgpack_eq(0, mapsp(sp('string', '["1"]'), '1'), '{"1": 1, "2": 2}') + end) + it('compares raw arrays correctly', function() + msgpack_eq(1, '[]', '[]') + msgpack_eq(0, '[]', '[1]') + msgpack_eq(1, '[1]', '[1]') + msgpack_eq(1, '[[[1]]]', '[[[1]]]') + msgpack_eq(0, '[[[2]]]', '[[[1]]]') + end) + it('compares array specials correctly', function() + msgpack_eq(1, sp('array', '[]'), sp('array', '[]')) + msgpack_eq(0, sp('array', '[]'), sp('array', '[1]')) + msgpack_eq(1, sp('array', '[1]'), sp('array', '[1]')) + msgpack_eq(1, sp('array', '[[[1]]]'), sp('array', '[[[1]]]')) + msgpack_eq(0, sp('array', '[[[1]]]'), sp('array', '[[[2]]]')) + end) + it('compares array specials with raw arrays correctly', function() + msgpack_eq(1, sp('array', '[]'), '[]') + msgpack_eq(0, sp('array', '[]'), '[1]') + msgpack_eq(1, sp('array', '[1]'), '[1]') + msgpack_eq(1, sp('array', '[[[1]]]'), '[[[1]]]') + msgpack_eq(0, sp('array', '[[[1]]]'), '[[[2]]]') + end) + it('compares raw floats correctly', function() + msgpack_eq(1, '0.0', '0.0') + msgpack_eq(1, '(1.0/0.0-1.0/0.0)', '(1.0/0.0-1.0/0.0)') + if has_minus_nan then + msgpack_eq(0, '(1.0/0.0-1.0/0.0)', '-(1.0/0.0-1.0/0.0)') + end + msgpack_eq(1, '-(1.0/0.0-1.0/0.0)', '-(1.0/0.0-1.0/0.0)') + msgpack_eq(1, '1.0/0.0', '1.0/0.0') + msgpack_eq(1, '-(1.0/0.0)', '-(1.0/0.0)') + msgpack_eq(1, '0.0', '0.0') + msgpack_eq(0, '0.0', '1.0') + msgpack_eq(0, '0.0', '(1.0/0.0-1.0/0.0)') + msgpack_eq(0, '0.0', '1.0/0.0') + msgpack_eq(0, '0.0', '-(1.0/0.0)') + msgpack_eq(0, '1.0/0.0', '-(1.0/0.0)') + msgpack_eq(0, '(1.0/0.0-1.0/0.0)', '-(1.0/0.0)') + msgpack_eq(0, '(1.0/0.0-1.0/0.0)', '1.0/0.0') + end) + it('compares float specials with raw floats correctly', function() + msgpack_eq(1, sp('float', '0.0'), '0.0') + msgpack_eq(1, sp('float', '(1.0/0.0-1.0/0.0)'), '(1.0/0.0-1.0/0.0)') + if has_minus_nan then + msgpack_eq(0, sp('float', '(1.0/0.0-1.0/0.0)'), '-(1.0/0.0-1.0/0.0)') + msgpack_eq(0, sp('float', '-(1.0/0.0-1.0/0.0)'), '(1.0/0.0-1.0/0.0)') + end + msgpack_eq(1, sp('float', '-(1.0/0.0-1.0/0.0)'), '-(1.0/0.0-1.0/0.0)') + msgpack_eq(1, sp('float', '1.0/0.0'), '1.0/0.0') + msgpack_eq(1, sp('float', '-(1.0/0.0)'), '-(1.0/0.0)') + msgpack_eq(1, sp('float', '0.0'), '0.0') + msgpack_eq(0, sp('float', '0.0'), '1.0') + msgpack_eq(0, sp('float', '0.0'), '(1.0/0.0-1.0/0.0)') + msgpack_eq(0, sp('float', '0.0'), '1.0/0.0') + msgpack_eq(0, sp('float', '0.0'), '-(1.0/0.0)') + msgpack_eq(0, sp('float', '1.0/0.0'), '-(1.0/0.0)') + msgpack_eq(0, sp('float', '(1.0/0.0-1.0/0.0)'), '-(1.0/0.0)') + msgpack_eq(0, sp('float', '(1.0/0.0-1.0/0.0)'), '1.0/0.0') + end) + it('compares float specials correctly', function() + msgpack_eq(1, sp('float', '0.0'), sp('float', '0.0')) + msgpack_eq(1, sp('float', '(1.0/0.0-1.0/0.0)'), + sp('float', '(1.0/0.0-1.0/0.0)')) + msgpack_eq(1, sp('float', '1.0/0.0'), sp('float', '1.0/0.0')) + msgpack_eq(1, sp('float', '-(1.0/0.0)'), sp('float', '-(1.0/0.0)')) + msgpack_eq(1, sp('float', '0.0'), sp('float', '0.0')) + msgpack_eq(0, sp('float', '0.0'), sp('float', '1.0')) + msgpack_eq(0, sp('float', '0.0'), sp('float', '(1.0/0.0-1.0/0.0)')) + msgpack_eq(0, sp('float', '0.0'), sp('float', '1.0/0.0')) + msgpack_eq(0, sp('float', '0.0'), sp('float', '-(1.0/0.0)')) + msgpack_eq(0, sp('float', '1.0/0.0'), sp('float', '-(1.0/0.0)')) + msgpack_eq(0, sp('float', '(1.0/0.0-1.0/0.0)'), sp('float', '-(1.0/0.0)')) + if has_minus_nan then + msgpack_eq(0, sp('float', '(1.0/0.0-1.0/0.0)'), + sp('float', '-(1.0/0.0-1.0/0.0)')) + end + msgpack_eq(1, sp('float', '-(1.0/0.0-1.0/0.0)'), + sp('float', '-(1.0/0.0-1.0/0.0)')) + msgpack_eq(0, sp('float', '(1.0/0.0-1.0/0.0)'), sp('float', '1.0/0.0')) + end) + it('compares boolean specials correctly', function() + msgpack_eq(1, sp('boolean', '1'), sp('boolean', '1')) + msgpack_eq(0, sp('boolean', '1'), sp('boolean', '0')) + end) + it('compares nil specials correctly', function() + msgpack_eq(1, sp('nil', '1'), sp('nil', '0')) + end) + it('compares nil, boolean and integer values with each other correctly', + function() + msgpack_eq(0, sp('boolean', '1'), '1') + msgpack_eq(0, sp('boolean', '1'), sp('nil', '0')) + msgpack_eq(0, sp('boolean', '1'), sp('nil', '1')) + msgpack_eq(0, sp('boolean', '0'), sp('nil', '0')) + msgpack_eq(0, sp('boolean', '0'), '0') + msgpack_eq(0, sp('boolean', '0'), sp('integer', '[1, 0, 0, 0]')) + msgpack_eq(0, sp('boolean', '0'), sp('integer', '[1, 0, 0, 1]')) + msgpack_eq(0, sp('boolean', '1'), sp('integer', '[1, 0, 0, 1]')) + msgpack_eq(0, sp('nil', '0'), sp('integer', '[1, 0, 0, 0]')) + msgpack_eq(0, sp('nil', '0'), '0') + end) + end) + + describe('function msgpack#is_int', function() + it('works', function() + eq(1, nvim_eval('msgpack#is_int(1)')) + eq(1, nvim_eval('msgpack#is_int(-1)')) + eq(1, nvim_eval(('msgpack#is_int(%s)'):format( + sp('integer', '[1, 0, 0, 1]')))) + eq(1, nvim_eval(('msgpack#is_int(%s)'):format( + sp('integer', '[-1, 0, 0, 1]')))) + eq(0, nvim_eval(('msgpack#is_int(%s)'):format( + sp('float', '0.0')))) + eq(0, nvim_eval(('msgpack#is_int(%s)'):format( + sp('boolean', '0')))) + eq(0, nvim_eval(('msgpack#is_int(%s)'):format( + sp('nil', '0')))) + eq(0, nvim_eval('msgpack#is_int("")')) + end) + end) + + describe('function msgpack#is_uint', function() + it('works', function() + eq(1, nvim_eval('msgpack#is_uint(1)')) + eq(0, nvim_eval('msgpack#is_uint(-1)')) + eq(1, nvim_eval(('msgpack#is_uint(%s)'):format( + sp('integer', '[1, 0, 0, 1]')))) + eq(0, nvim_eval(('msgpack#is_uint(%s)'):format( + sp('integer', '[-1, 0, 0, 1]')))) + eq(0, nvim_eval(('msgpack#is_uint(%s)'):format( + sp('float', '0.0')))) + eq(0, nvim_eval(('msgpack#is_uint(%s)'):format( + sp('boolean', '0')))) + eq(0, nvim_eval(('msgpack#is_uint(%s)'):format( + sp('nil', '0')))) + eq(0, nvim_eval('msgpack#is_uint("")')) + end) + end) + + describe('function msgpack#strftime', function() + it('works', function() + local epoch = os.date('%Y-%m-%dT%H:%M:%S', 0) + eq(epoch, nvim_eval('msgpack#strftime("%Y-%m-%dT%H:%M:%S", 0)')) + eq(epoch, nvim_eval( + ('msgpack#strftime("%%Y-%%m-%%dT%%H:%%M:%%S", %s)'):format(sp( + 'integer', '[1, 0, 0, 0]')))) + end) + end) + + describe('function msgpack#strptime', function() + it('works', function() + for _, v in ipairs({0, 10, 100000, 204, 1000000000}) do + local time = os.date('%Y-%m-%dT%H:%M:%S', v) + eq(v, nvim_eval('msgpack#strptime("%Y-%m-%dT%H:%M:%S", ' + .. '"' .. time .. '")')) + end + end) + end) + + describe('function msgpack#type', function() + local type_eq = function(expected, val) + eq(expected, nvim_eval(('msgpack#type(%s)'):format(val))) + end + + it('works for special dictionaries', function() + type_eq('string', sp('string', '[""]')) + type_eq('binary', sp('binary', '[""]')) + type_eq('ext', sp('ext', '[1, [""]]')) + type_eq('array', sp('array', '[]')) + type_eq('map', sp('map', '[]')) + type_eq('integer', sp('integer', '[1, 0, 0, 0]')) + type_eq('float', sp('float', '0.0')) + type_eq('boolean', sp('boolean', '0')) + type_eq('nil', sp('nil', '0')) + end) + + it('works for regular values', function() + type_eq('binary', '""') + type_eq('array', '[]') + type_eq('map', '{}') + type_eq('integer', '1') + type_eq('float', '0.0') + type_eq('float', '(1.0/0.0)') + type_eq('float', '-(1.0/0.0)') + type_eq('float', '(1.0/0.0-1.0/0.0)') + end) + end) + + describe('function msgpack#special_type', function() + local sp_type_eq = function(expected, val) + eq(expected, nvim_eval(('msgpack#special_type(%s)'):format(val))) + end + + it('works for special dictionaries', function() + sp_type_eq('string', sp('string', '[""]')) + sp_type_eq('binary', sp('binary', '[""]')) + sp_type_eq('ext', sp('ext', '[1, [""]]')) + sp_type_eq('array', sp('array', '[]')) + sp_type_eq('map', sp('map', '[]')) + sp_type_eq('integer', sp('integer', '[1, 0, 0, 0]')) + sp_type_eq('float', sp('float', '0.0')) + sp_type_eq('boolean', sp('boolean', '0')) + sp_type_eq('nil', sp('nil', '0')) + end) + + it('works for regular values', function() + sp_type_eq(0, '""') + sp_type_eq(0, '[]') + sp_type_eq(0, '{}') + sp_type_eq(0, '1') + sp_type_eq(0, '0.0') + sp_type_eq(0, '(1.0/0.0)') + sp_type_eq(0, '-(1.0/0.0)') + sp_type_eq(0, '(1.0/0.0-1.0/0.0)') + end) + end) + + describe('function msgpack#string', function() + local string_eq = function(expected, val) + eq(expected, nvim_eval(('msgpack#string(%s)'):format(val))) + end + + it('works for special dictionaries', function() + string_eq('=""', sp('string', '[""]')) + string_eq('="\\n"', sp('string', '["", ""]')) + string_eq('="ab\\0c\\nde"', sp('string', '["ab\\nc", "de"]')) + string_eq('""', sp('binary', '[""]')) + string_eq('"\\n"', sp('binary', '["", ""]')) + string_eq('"ab\\0c\\nde"', sp('binary', '["ab\\nc", "de"]')) + string_eq('+(2)""', sp('ext', '[2, [""]]')) + string_eq('+(2)"\\n"', sp('ext', '[2, ["", ""]]')) + string_eq('+(2)"ab\\0c\\nde"', sp('ext', '[2, ["ab\\nc", "de"]]')) + string_eq('[]', sp('array', '[]')) + string_eq('[[[[{}]]]]', sp('array', '[[[[{}]]]]')) + string_eq('{}', sp('map', '[]')) + string_eq('{2: 10}', sp('map', '[[2, 10]]')) + string_eq('{{1: 1}: {1: 1}, {2: 1}: {1: 1}}', + mapsp(mapsp('2', '1'), mapsp('1', '1'), + mapsp('1', '1'), mapsp('1', '1'))) + string_eq('{{1: 1}: {1: 1}, {2: 1}: {1: 1}}', + mapsp(mapsp('1', '1'), mapsp('1', '1'), + mapsp('2', '1'), mapsp('1', '1'))) + string_eq('{[1, 2, {{1: 2}: 1}]: [1, 2, {{1: 2}: 1}]}', + mapsp(('[1, 2, %s]'):format(mapsp(mapsp('1', '2'), '1')), + ('[1, 2, %s]'):format(mapsp(mapsp('1', '2'), '1')))) + string_eq('0x0000000000000000', sp('integer', '[1, 0, 0, 0]')) + string_eq('-0x0000000100000000', sp('integer', '[-1, 0, 2, 0]')) + string_eq('0x123456789abcdef0', + sp('integer', '[ 1, 0, 610839793, 448585456]')) + string_eq('-0x123456789abcdef0', + sp('integer', '[-1, 0, 610839793, 448585456]')) + string_eq('0xf23456789abcdef0', + sp('integer', '[ 1, 3, 1684581617, 448585456]')) + string_eq('-0x723456789abcdef0', + sp('integer', '[-1, 1, 1684581617, 448585456]')) + string_eq('0.0', sp('float', '0.0')) + string_eq('inf', sp('float', '(1.0/0.0)')) + string_eq('-inf', sp('float', '-(1.0/0.0)')) + if has_minus_nan then + string_eq('-nan', sp('float', '(1.0/0.0-1.0/0.0)')) + end + string_eq('nan', sp('float', '-(1.0/0.0-1.0/0.0)')) + string_eq('FALSE', sp('boolean', '0')) + string_eq('TRUE', sp('boolean', '1')) + string_eq('NIL', sp('nil', '0')) + end) + + it('works for regular values', function() + string_eq('""', '""') + string_eq('"\\n"', '"\\n"') + string_eq('[]', '[]') + string_eq('[[[{}]]]', '[[[{}]]]') + string_eq('{}', '{}') + string_eq('{="2": 10}', '{2: 10}') + string_eq('{="2": [{}]}', '{2: [{}]}') + string_eq('1', '1') + string_eq('0.0', '0.0') + string_eq('inf', '(1.0/0.0)') + string_eq('-inf', '-(1.0/0.0)') + if has_minus_nan then + string_eq('-nan', '(1.0/0.0-1.0/0.0)') + end + string_eq('nan', '-(1.0/0.0-1.0/0.0)') + end) + end) + + describe('function msgpack#deepcopy', function() + it('works for special dictionaries', function() + nvim_command('let sparr = ' .. sp('array', '[[[]]]')) + nvim_command('let spmap = ' .. mapsp('"abc"', '[[]]')) + nvim_command('let spint = ' .. sp('integer', '[1, 0, 0, 0]')) + nvim_command('let spflt = ' .. sp('float', '1.0')) + nvim_command('let spext = ' .. sp('ext', '[2, ["abc", "def"]]')) + nvim_command('let spstr = ' .. sp('string', '["abc", "def"]')) + nvim_command('let spbin = ' .. sp('binary', '["abc", "def"]')) + nvim_command('let spbln = ' .. sp('boolean', '0')) + nvim_command('let spnil = ' .. sp('nil', '0')) + + nvim_command('let sparr2 = msgpack#deepcopy(sparr)') + nvim_command('let spmap2 = msgpack#deepcopy(spmap)') + nvim_command('let spint2 = msgpack#deepcopy(spint)') + nvim_command('let spflt2 = msgpack#deepcopy(spflt)') + nvim_command('let spext2 = msgpack#deepcopy(spext)') + nvim_command('let spstr2 = msgpack#deepcopy(spstr)') + nvim_command('let spbin2 = msgpack#deepcopy(spbin)') + nvim_command('let spbln2 = msgpack#deepcopy(spbln)') + nvim_command('let spnil2 = msgpack#deepcopy(spnil)') + + eq('array', nvim_eval('msgpack#type(sparr2)')) + eq('map', nvim_eval('msgpack#type(spmap2)')) + eq('integer', nvim_eval('msgpack#type(spint2)')) + eq('float', nvim_eval('msgpack#type(spflt2)')) + eq('ext', nvim_eval('msgpack#type(spext2)')) + eq('string', nvim_eval('msgpack#type(spstr2)')) + eq('binary', nvim_eval('msgpack#type(spbin2)')) + eq('boolean', nvim_eval('msgpack#type(spbln2)')) + eq('nil', nvim_eval('msgpack#type(spnil2)')) + + nvim_command('call add(sparr._VAL, 0)') + nvim_command('call add(sparr._VAL[0], 0)') + nvim_command('call add(sparr._VAL[0][0], 0)') + nvim_command('call add(spmap._VAL, [0, 0])') + nvim_command('call add(spmap._VAL[0][1], 0)') + nvim_command('call add(spmap._VAL[0][1][0], 0)') + nvim_command('let spint._VAL[1] = 1') + nvim_command('let spflt._VAL = 0.0') + nvim_command('let spext._VAL[0] = 3') + nvim_command('let spext._VAL[1][0] = "gh"') + nvim_command('let spstr._VAL[0] = "gh"') + nvim_command('let spbin._VAL[0] = "gh"') + nvim_command('let spbln._VAL = 1') + nvim_command('let spnil._VAL = 1') + + eq({_TYPE={}, _VAL={{{}}}}, nvim_eval('sparr2')) + eq({_TYPE={}, _VAL={{'abc', {{}}}}}, nvim_eval('spmap2')) + eq({_TYPE={}, _VAL={1, 0, 0, 0}}, nvim_eval('spint2')) + eq({_TYPE={}, _VAL=1.0}, nvim_eval('spflt2')) + eq({_TYPE={}, _VAL={2, {'abc', 'def'}}}, nvim_eval('spext2')) + eq({_TYPE={}, _VAL={'abc', 'def'}}, nvim_eval('spstr2')) + eq({_TYPE={}, _VAL={'abc', 'def'}}, nvim_eval('spbin2')) + eq({_TYPE={}, _VAL=0}, nvim_eval('spbln2')) + eq({_TYPE={}, _VAL=0}, nvim_eval('spnil2')) + + nvim_command('let sparr._TYPE = []') + nvim_command('let spmap._TYPE = []') + nvim_command('let spint._TYPE = []') + nvim_command('let spflt._TYPE = []') + nvim_command('let spext._TYPE = []') + nvim_command('let spstr._TYPE = []') + nvim_command('let spbin._TYPE = []') + nvim_command('let spbln._TYPE = []') + nvim_command('let spnil._TYPE = []') + + eq('array', nvim_eval('msgpack#special_type(sparr2)')) + eq('map', nvim_eval('msgpack#special_type(spmap2)')) + eq('integer', nvim_eval('msgpack#special_type(spint2)')) + eq('float', nvim_eval('msgpack#special_type(spflt2)')) + eq('ext', nvim_eval('msgpack#special_type(spext2)')) + eq('string', nvim_eval('msgpack#special_type(spstr2)')) + eq('binary', nvim_eval('msgpack#special_type(spbin2)')) + eq('boolean', nvim_eval('msgpack#special_type(spbln2)')) + eq('nil', nvim_eval('msgpack#special_type(spnil2)')) + end) + + it('works for regular values', function() + nvim_command('let arr = [[[]]]') + nvim_command('let map = {1: {}}') + nvim_command('let int = 1') + nvim_command('let flt = 2.0') + nvim_command('let bin = "abc"') + + nvim_command('let arr2 = msgpack#deepcopy(arr)') + nvim_command('let map2 = msgpack#deepcopy(map)') + nvim_command('let int2 = msgpack#deepcopy(int)') + nvim_command('let flt2 = msgpack#deepcopy(flt)') + nvim_command('let bin2 = msgpack#deepcopy(bin)') + + eq('array', nvim_eval('msgpack#type(arr2)')) + eq('map', nvim_eval('msgpack#type(map2)')) + eq('integer', nvim_eval('msgpack#type(int2)')) + eq('float', nvim_eval('msgpack#type(flt2)')) + eq('binary', nvim_eval('msgpack#type(bin2)')) + + nvim_command('call add(arr, 0)') + nvim_command('call add(arr[0], 0)') + nvim_command('call add(arr[0][0], 0)') + nvim_command('let map.a = 1') + nvim_command('let map.1.a = 1') + nvim_command('let int = 2') + nvim_command('let flt = 3.0') + nvim_command('let bin = ""') + + eq({{{}}}, nvim_eval('arr2')) + eq({['1']={}}, nvim_eval('map2')) + eq(1, nvim_eval('int2')) + eq(2.0, nvim_eval('flt2')) + eq('abc', nvim_eval('bin2')) + end) + end) + + describe('function msgpack#eval', function() + local eval_eq = function(expected_type, expected_val, str, ...) + nvim_command(('let g:__val = msgpack#eval(\'%s\', %s)'):format(str:gsub( + '\'', '\'\''), select(1, ...) or '{}')) + eq(expected_type, nvim_eval('msgpack#type(g:__val)')) + local expected_val_full = expected_val + if (not (({float=true, integer=true})[expected_type] + and type(expected_val) ~= 'table') + and expected_type ~= 'array') then + expected_val_full = {_TYPE={}, _VAL=expected_val_full} + end + if expected_val_full == expected_val_full then + eq(expected_val_full, nvim_eval('g:__val')) + else + eq(tostring(expected_val_full), tostring(nvim_eval('g:__val'))) + end + nvim_command('unlet g:__val') + end + + it('correctly loads binary strings', function() + eval_eq('binary', {'abcdef'}, '"abcdef"') + eval_eq('binary', {'abc', 'def'}, '"abc\\ndef"') + eval_eq('binary', {'abc\ndef'}, '"abc\\0def"') + eval_eq('binary', {'\nabc\ndef\n'}, '"\\0abc\\0def\\0"') + eval_eq('binary', {'abc\n\n\ndef'}, '"abc\\0\\0\\0def"') + eval_eq('binary', {'abc\n', '\ndef'}, '"abc\\0\\n\\0def"') + eval_eq('binary', {'abc', '', '', 'def'}, '"abc\\n\\n\\ndef"') + eval_eq('binary', {'abc', '', '', 'def', ''}, '"abc\\n\\n\\ndef\\n"') + eval_eq('binary', {'', 'abc', '', '', 'def'}, '"\\nabc\\n\\n\\ndef"') + eval_eq('binary', {''}, '""') + eval_eq('binary', {'"'}, '"\\""') + end) + + it('correctly loads strings', function() + eval_eq('string', {'abcdef'}, '="abcdef"') + eval_eq('string', {'abc', 'def'}, '="abc\\ndef"') + eval_eq('string', {'abc\ndef'}, '="abc\\0def"') + eval_eq('string', {'\nabc\ndef\n'}, '="\\0abc\\0def\\0"') + eval_eq('string', {'abc\n\n\ndef'}, '="abc\\0\\0\\0def"') + eval_eq('string', {'abc\n', '\ndef'}, '="abc\\0\\n\\0def"') + eval_eq('string', {'abc', '', '', 'def'}, '="abc\\n\\n\\ndef"') + eval_eq('string', {'abc', '', '', 'def', ''}, '="abc\\n\\n\\ndef\\n"') + eval_eq('string', {'', 'abc', '', '', 'def'}, '="\\nabc\\n\\n\\ndef"') + eval_eq('string', {''}, '=""') + eval_eq('string', {'"'}, '="\\""') + end) + + it('correctly loads ext values', function() + eval_eq('ext', {0, {'abcdef'}}, '+(0)"abcdef"') + eval_eq('ext', {0, {'abc', 'def'}}, '+(0)"abc\\ndef"') + eval_eq('ext', {0, {'abc\ndef'}}, '+(0)"abc\\0def"') + eval_eq('ext', {0, {'\nabc\ndef\n'}}, '+(0)"\\0abc\\0def\\0"') + eval_eq('ext', {0, {'abc\n\n\ndef'}}, '+(0)"abc\\0\\0\\0def"') + eval_eq('ext', {0, {'abc\n', '\ndef'}}, '+(0)"abc\\0\\n\\0def"') + eval_eq('ext', {0, {'abc', '', '', 'def'}}, '+(0)"abc\\n\\n\\ndef"') + eval_eq('ext', {0, {'abc', '', '', 'def', ''}}, + '+(0)"abc\\n\\n\\ndef\\n"') + eval_eq('ext', {0, {'', 'abc', '', '', 'def'}}, + '+(0)"\\nabc\\n\\n\\ndef"') + eval_eq('ext', {0, {''}}, '+(0)""') + eval_eq('ext', {0, {'"'}}, '+(0)"\\""') + + eval_eq('ext', {-1, {'abcdef'}}, '+(-1)"abcdef"') + eval_eq('ext', {-1, {'abc', 'def'}}, '+(-1)"abc\\ndef"') + eval_eq('ext', {-1, {'abc\ndef'}}, '+(-1)"abc\\0def"') + eval_eq('ext', {-1, {'\nabc\ndef\n'}}, '+(-1)"\\0abc\\0def\\0"') + eval_eq('ext', {-1, {'abc\n\n\ndef'}}, '+(-1)"abc\\0\\0\\0def"') + eval_eq('ext', {-1, {'abc\n', '\ndef'}}, '+(-1)"abc\\0\\n\\0def"') + eval_eq('ext', {-1, {'abc', '', '', 'def'}}, '+(-1)"abc\\n\\n\\ndef"') + eval_eq('ext', {-1, {'abc', '', '', 'def', ''}}, + '+(-1)"abc\\n\\n\\ndef\\n"') + eval_eq('ext', {-1, {'', 'abc', '', '', 'def'}}, + '+(-1)"\\nabc\\n\\n\\ndef"') + eval_eq('ext', {-1, {''}}, '+(-1)""') + eval_eq('ext', {-1, {'"'}}, '+(-1)"\\""') + end) + + it('correctly loads floats', function() + eval_eq('float', inf, 'inf') + eval_eq('float', minus_inf, '-inf') + eval_eq('float', nan, 'nan') + eval_eq('float', minus_nan, '-nan') + eval_eq('float', 1.0e10, '1.0e10') + eval_eq('float', 1.0e10, '1.0e+10') + eval_eq('float', -1.0e10, '-1.0e+10') + eval_eq('float', 1.0, '1.0') + eval_eq('float', -1.0, '-1.0') + eval_eq('float', 1.0e-10, '1.0e-10') + eval_eq('float', -1.0e-10, '-1.0e-10') + end) + + it('correctly loads integers', function() + eval_eq('integer', 10, '10') + eval_eq('integer', -10, '-10') + eval_eq('integer', { 1, 0, 610839793, 448585456}, ' 0x123456789ABCDEF0') + eval_eq('integer', {-1, 0, 610839793, 448585456}, '-0x123456789ABCDEF0') + eval_eq('integer', { 1, 3, 1684581617, 448585456}, ' 0xF23456789ABCDEF0') + eval_eq('integer', {-1, 1, 1684581617, 448585456}, '-0x723456789ABCDEF0') + eval_eq('integer', { 1, 0, 0, 0x100}, '0x100') + eval_eq('integer', {-1, 0, 0, 0x100}, '-0x100') + + eval_eq('integer', ('a'):byte(), '\'a\'') + eval_eq('integer', 0xAB, '\'«\'') + end) + + it('correctly loads constants', function() + eval_eq('boolean', 1, 'TRUE') + eval_eq('boolean', 0, 'FALSE') + eval_eq('nil', 0, 'NIL') + eval_eq('nil', 0, 'NIL', '{"NIL": 1, "nan": 2, "T": 3}') + eval_eq('float', nan, 'nan', + '{"NIL": "1", "nan": "2", "T": "3"}') + eval_eq('integer', 3, 'T', '{"NIL": "1", "nan": "2", "T": "3"}') + eval_eq('integer', {1, 0, 0, 0}, 'T', + ('{"NIL": "1", "nan": "2", "T": \'%s\'}'):format( + sp('integer', '[1, 0, 0, 0]'))) + end) + + it('correctly loads maps', function() + eval_eq('map', {}, '{}') + eval_eq('map', {{{_TYPE={}, _VAL={{1, 2}}}, {_TYPE={}, _VAL={{3, 4}}}}}, + '{{1: 2}: {3: 4}}') + eval_eq('map', {{{_TYPE={}, _VAL={{1, 2}}}, {_TYPE={}, _VAL={{3, 4}}}}, + {1, 2}}, + '{{1: 2}: {3: 4}, 1: 2}') + end) + + it('correctly loads arrays', function() + eval_eq('array', {}, '[]') + eval_eq('array', {1}, '[1]') + eval_eq('array', {{_TYPE={}, _VAL=1}}, '[TRUE]') + eval_eq('array', {{{_TYPE={}, _VAL={{1, 2}}}}, {_TYPE={}, _VAL={{3, 4}}}}, + '[[{1: 2}], {3: 4}]') + end) + + it('errors out when needed', function() + eq('empty:Parsed string is empty', + exc_exec('call msgpack#eval("", {})')) + eq('unknown:Invalid non-space character: ^', + exc_exec('call msgpack#eval("^", {})')) + eq('char-invalid:Invalid integer character literal format: \'\'', + exc_exec('call msgpack#eval("\'\'", {})')) + eq('char-invalid:Invalid integer character literal format: \'ab\'', + exc_exec('call msgpack#eval("\'ab\'", {})')) + eq('char-invalid:Invalid integer character literal format: \'', + exc_exec('call msgpack#eval("\'", {})')) + eq('"-invalid:Invalid string: "', + exc_exec('call msgpack#eval("\\"", {})')) + eq('"-invalid:Invalid string: ="', + exc_exec('call msgpack#eval("=\\"", {})')) + eq('"-invalid:Invalid string: +(0)"', + exc_exec('call msgpack#eval("+(0)\\"", {})')) + eq('0.-nodigits:Decimal dot must be followed by digit(s): .e1', + exc_exec('call msgpack#eval("0.e1", {})')) + eq('0x-long:Must have at most 16 hex digits: FEDCBA98765432100', + exc_exec('call msgpack#eval("0xFEDCBA98765432100", {})')) + eq('0x-empty:Must have number after 0x: ', + exc_exec('call msgpack#eval("0x", {})')) + eq('name-unknown:Unknown name FOO: FOO', + exc_exec('call msgpack#eval("FOO", {})')) + end) + end) +end) diff --git a/test/functional/plugin/shada_spec.lua b/test/functional/plugin/shada_spec.lua new file mode 100644 index 0000000000..407f13a55b --- /dev/null +++ b/test/functional/plugin/shada_spec.lua @@ -0,0 +1,2833 @@ +local helpers = require('test.functional.helpers') +local eq, nvim_eval, nvim_command, nvim, exc_exec, funcs, nvim_feed, curbuf = + helpers.eq, helpers.eval, helpers.command, helpers.nvim, helpers.exc_exec, + helpers.funcs, helpers.feed, helpers.curbuf +local neq = helpers.neq + +local msgpack = require('MessagePack') + +local plugin_helpers = require('test.functional.plugin.helpers') +local reset = plugin_helpers.reset + +local shada_helpers = require('test.functional.shada.helpers') +local get_shada_rw = shada_helpers.get_shada_rw +local read_shada_file = shada_helpers.read_shada_file + +local mpack_eq = function(expected, mpack_result) + local mpack_keys = {'type', 'timestamp', 'length', 'value'} + + local unpacker = msgpack.unpacker(mpack_result) + local actual = {} + local cur + local i = 0 + while true do + local off, val = unpacker() + if not off then break end + if i % 4 == 0 then + cur = {} + actual[#actual + 1] = cur + end + local key = mpack_keys[(i % 4) + 1] + if key ~= 'length' then + if key == 'timestamp' and math.abs(val - os.time()) < 2 then + val = 'current' + end + cur[key] = val + end + i = i + 1 + end + eq(expected, actual) +end + +local wshada, _, fname = get_shada_rw('Xtest-functional-plugin-shada.shada') + +local wshada_tmp, _, fname_tmp = + get_shada_rw('Xtest-functional-plugin-shada.shada.tmp.f') + +describe('In autoload/shada.vim', function() + local epoch = os.date('%Y-%m-%dT%H:%M:%S', 0) + before_each(function() + reset() + nvim_command([[ + function ModifyVal(val) + if type(a:val) == type([]) + if len(a:val) == 2 && type(a:val[0]) == type('') && a:val[0][0] is# '!' && has_key(v:msgpack_types, a:val[0][1:]) + return {'_TYPE': v:msgpack_types[ a:val[0][1:] ], '_VAL': a:val[1]} + else + return map(copy(a:val), 'ModifyVal(v:val)') + endif + elseif type(a:val) == type({}) + let keys = sort(keys(a:val)) + let ret = {'_TYPE': v:msgpack_types.map, '_VAL': []} + for key in keys + let k = {'_TYPE': v:msgpack_types.string, '_VAL': split(key, "\n", 1)} + let v = ModifyVal(a:val[key]) + call add(ret._VAL, [k, v]) + unlet v + endfor + return ret + elseif type(a:val) == type('') + return {'_TYPE': v:msgpack_types.binary, '_VAL': split(a:val, "\n", 1)} + else + return a:val + endif + endfunction + ]]) + end) + + local sp = function(typ, val) + return ('{"_TYPE": v:msgpack_types.%s, "_VAL": %s}'):format(typ, val) + end + + local st_meta = { + __pairs=function(table) + local ret = {} + local next_key = nil + local num_keys = 0 + while true do + next_key = next(table, next_key) + if next_key == nil then + break + end + num_keys = num_keys + 1 + ret[num_keys] = {next_key, table[next_key]} + end + table.sort(ret, function(a, b) + return a[1] < b[1] + end) + local state = {i=0} + return (function(state, var) + state.i = state.i + 1 + if ret[state.i] then + return table.unpack(ret[state.i]) + end + end), state + end + } + + local st = function(table) + return setmetatable(table, st_meta) + end + + describe('function shada#mpack_to_sd', function() + local mpack2sd = function(arg) + return ('shada#mpack_to_sd(%s)'):format(arg) + end + + it('works', function() + eq({}, nvim_eval(mpack2sd('[]'))) + eq({{type=1, timestamp=5, length=1, data=7}}, + nvim_eval(mpack2sd('[1, 5, 1, 7]'))) + eq({{type=1, timestamp=5, length=1, data=7}, + {type=1, timestamp=10, length=1, data=5}}, + nvim_eval(mpack2sd('[1, 5, 1, 7, 1, 10, 1, 5]'))) + eq('zero-uint:Entry 1 has type element which is zero', + exc_exec('call ' .. mpack2sd('[0, 5, 1, 7]'))) + eq('zero-uint:Entry 1 has type element which is zero', + exc_exec('call ' .. mpack2sd(('[%s, 5, 1, 7]'):format( + sp('integer', '[1, 0, 0, 0]'))))) + eq('not-uint:Entry 1 has timestamp element which is not an unsigned integer', + exc_exec('call ' .. mpack2sd('[1, -1, 1, 7]'))) + eq('not-uint:Entry 1 has length element which is not an unsigned integer', + exc_exec('call ' .. mpack2sd('[1, 1, -1, 7]'))) + eq('not-uint:Entry 1 has type element which is not an unsigned integer', + exc_exec('call ' .. mpack2sd('["", 1, -1, 7]'))) + end) + end) + + describe('function shada#sd_to_strings', function() + local sd2strings_eq = function(expected, arg) + if type(arg) == 'table' then + eq(expected, funcs['shada#sd_to_strings'](arg)) + else + eq(expected, nvim_eval(('shada#sd_to_strings(%s)'):format(arg))) + end + end + + it('works with empty input', function() + sd2strings_eq({}, '[]') + end) + + it('works with unknown items', function() + sd2strings_eq({ + 'Unknown (0x64) with timestamp ' .. epoch .. ':', + ' = 100' + }, {{type=100, timestamp=0, length=1, data=100}}) + + sd2strings_eq({ + 'Unknown (0x4000001180000006) with timestamp ' .. epoch .. ':', + ' = 100' + }, ('[{"type": %s, "timestamp": 0, "length": 1, "data": 100}]'):format( + sp('integer', '[1, 1, 35, 6]') + )) + end) + + it('works with multiple unknown items', function() + sd2strings_eq({ + 'Unknown (0x64) with timestamp ' .. epoch .. ':', + ' = 100', + 'Unknown (0x65) with timestamp ' .. epoch .. ':', + ' = 500', + }, {{type=100, timestamp=0, length=1, data=100}, + {type=101, timestamp=0, length=1, data=500}}) + end) + + it('works with header items', function() + sd2strings_eq({ + 'Header with timestamp ' .. epoch .. ':', + ' % Key______ Value', + ' + generator "test"', + }, {{type=1, timestamp=0, data={generator='test'}}}) + sd2strings_eq({ + 'Header with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + a 1', + ' + b 2', + ' + c column 3', + ' + d 4', + }, {{type=1, timestamp=0, data=st({a=1, b=2, c=3, d=4})}}) + sd2strings_eq({ + 'Header with timestamp ' .. epoch .. ':', + ' % Key Value', + ' + t "test"', + }, {{type=1, timestamp=0, data={t='test'}}}) + sd2strings_eq({ + 'Header with timestamp ' .. epoch .. ':', + ' # Unexpected type: array instead of map', + ' = [1, 2, 3]', + }, {{type=1, timestamp=0, data={1, 2, 3}}}) + end) + + it('processes standard keys correctly, even in header', function() + sd2strings_eq({ + 'Header with timestamp ' .. epoch .. ':', + ' % Key Description________ Value', + ' + c column 0', + ' + f file name "/tmp/foo"', + ' + l line number 10', + ' + n name \'@\'', + ' + rc contents ["abc", "def"]', + ' + rt type CHARACTERWISE', + ' + rw block width 10', + ' + sc smartcase value FALSE', + ' + se place cursor at end TRUE', + ' + sh v:hlsearch value TRUE', + ' + sl has line offset FALSE', + ' + sm magic value TRUE', + ' + so offset value 10', + ' + sp pattern "100"', + ' + ss is :s pattern TRUE', + ' + su is last used FALSE', + }, ([[ [{'type': 1, 'timestamp': 0, 'data': { + 'sm': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1}, + 'sc': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0}, + 'sl': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0}, + 'se': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1}, + 'so': 10, + 'su': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0}, + 'ss': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1}, + 'sh': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1}, + 'sp': '100', + 'rt': 0, + 'rw': 10, + 'rc': ['abc', 'def'], + 'n': 0x40, + 'l': 10, + 'c': 0, + 'f': '/tmp/foo', + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Header with timestamp ' .. epoch .. ':', + ' % Key Description____ Value', + ' # Expected integer', + ' + c column "abc"', + ' # Expected no NUL bytes', + ' + f file name "abc\\0def"', + ' # Value is negative', + ' + l line number -10', + ' # Value is negative', + ' + n name -64', + ' # Expected array value', + ' + rc contents "10"', + ' # Unexpected enum value: expected one of ' + .. '0 (CHARACTERWISE), 1 (LINEWISE), 2 (BLOCKWISE)', + ' + rt type 10', + ' # Expected boolean', + ' + sc smartcase value NIL', + ' # Expected boolean', + ' + sm magic value "TRUE"', + ' # Expected integer', + ' + so offset value "TRUE"', + ' # Expected binary string', + ' + sp pattern ="abc"', + }, ([[ [{'type': 1, 'timestamp': 0, 'data': { + 'sm': 'TRUE', + 'sc': {'_TYPE': v:msgpack_types.nil, '_VAL': 0}, + 'so': 'TRUE', + 'sp': {'_TYPE': v:msgpack_types.string, '_VAL': ["abc"]}, + 'rt': 10, + 'rc': '10', + 'n': -0x40, + 'l': -10, + 'c': 'abc', + 'f': {'_TYPE': v:msgpack_types.binary, '_VAL': ["abc\ndef"]}, + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Header with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Expected no NUL bytes', + ' + f file name "abc\\0def"', + ' # Expected array of binary strings', + ' + rc contents ["abc", ="abc"]', + ' # Expected integer', + ' + rt type "ABC"', + }, ([[ [{'type': 1, 'timestamp': 0, 'data': { + 'rt': 'ABC', + 'rc': ["abc", {'_TYPE': v:msgpack_types.string, '_VAL': ["abc"]}], + 'f': {'_TYPE': v:msgpack_types.binary, '_VAL': ["abc\ndef"]}, + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Header with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Expected no NUL bytes', + ' + rc contents ["abc", "a\\nd\\0"]', + }, ([[ [{'type': 1, 'timestamp': 0, 'data': { + 'rc': ["abc", {'_TYPE': v:msgpack_types.binary, '_VAL': ["a", "d\n"]}], + }}] ]]):gsub('\n', '')) + end) + + it('works with search pattern items', function() + sd2strings_eq({ + 'Search pattern with timestamp ' .. epoch .. ':', + ' # Unexpected type: array instead of map', + ' = [1, 2, 3]', + }, {{type=2, timestamp=0, data={1, 2, 3}}}) + sd2strings_eq({ + 'Search pattern with timestamp ' .. epoch .. ':', + ' % Key Description________ Value', + ' + sp pattern "abc"', + ' + sh v:hlsearch value FALSE', + ' + ss is :s pattern FALSE', + ' + sm magic value TRUE', + ' + sc smartcase value FALSE', + ' + sl has line offset FALSE', + ' + se place cursor at end FALSE', + ' + so offset value 0', + ' + su is last used TRUE', + }, ([[ [{'type': 2, 'timestamp': 0, 'data': { + 'sp': 'abc', + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Search pattern with timestamp ' .. epoch .. ':', + ' % Key Description________ Value', + ' + sp pattern "abc"', + ' + sh v:hlsearch value FALSE', + ' + ss is :s pattern FALSE', + ' + sm magic value TRUE', + ' + sc smartcase value FALSE', + ' + sl has line offset FALSE', + ' + se place cursor at end FALSE', + ' + so offset value 0', + ' + su is last used TRUE', + ' + sX NIL', + ' + sY NIL', + ' + sZ NIL', + }, ([[ [{'type': 2, 'timestamp': 0, 'data': { + 'sp': 'abc', + 'sZ': {'_TYPE': v:msgpack_types.nil, '_VAL': 0}, + 'sY': {'_TYPE': v:msgpack_types.nil, '_VAL': 0}, + 'sX': {'_TYPE': v:msgpack_types.nil, '_VAL': 0}, + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Search pattern with timestamp ' .. epoch .. ':', + ' % Key Description________ Value', + ' + sp pattern "abc"', + ' + sh v:hlsearch value FALSE', + ' + ss is :s pattern FALSE', + ' + sm magic value TRUE', + ' + sc smartcase value FALSE', + ' + sl has line offset FALSE', + ' + se place cursor at end FALSE', + ' + so offset value 0', + ' + su is last used TRUE', + }, ([[ [{'type': 2, 'timestamp': 0, 'data': { + 'sp': 'abc', + 'sh': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0}, + 'ss': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0}, + 'sm': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1}, + 'sc': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0}, + 'sl': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0}, + 'se': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0}, + 'so': 0, + 'su': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1}, + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Search pattern with timestamp ' .. epoch .. ':', + ' % Key Description________ Value', + ' # Required key missing: sp', + ' + sh v:hlsearch value FALSE', + ' + ss is :s pattern FALSE', + ' + sm magic value TRUE', + ' + sc smartcase value FALSE', + ' + sl has line offset FALSE', + ' + se place cursor at end FALSE', + ' + so offset value 0', + ' + su is last used TRUE', + }, ([[ [{'type': 2, 'timestamp': 0, 'data': { + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Search pattern with timestamp ' .. epoch .. ':', + ' % Key Description________ Value', + ' + sp pattern ""', + ' + sh v:hlsearch value TRUE', + ' + ss is :s pattern TRUE', + ' + sm magic value FALSE', + ' + sc smartcase value TRUE', + ' + sl has line offset TRUE', + ' + se place cursor at end TRUE', + ' + so offset value -10', + ' + su is last used FALSE', + }, ([[ [{'type': 2, 'timestamp': 0, 'data': { + 'sp': '', + 'sh': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1}, + 'ss': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1}, + 'sm': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0}, + 'sc': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1}, + 'sl': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1}, + 'se': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1}, + 'so': -10, + 'su': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0}, + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Search pattern with timestamp ' .. epoch .. ':', + ' % Key Description________ Value', + ' # Expected binary string', + ' + sp pattern 0', + ' # Expected boolean', + ' + sh v:hlsearch value 0', + ' # Expected boolean', + ' + ss is :s pattern 0', + ' # Expected boolean', + ' + sm magic value 0', + ' # Expected boolean', + ' + sc smartcase value 0', + ' # Expected boolean', + ' + sl has line offset 0', + ' # Expected boolean', + ' + se place cursor at end 0', + ' # Expected integer', + ' + so offset value ""', + ' # Expected boolean', + ' + su is last used 0', + }, ([[ [{'type': 2, 'timestamp': 0, 'data': { + 'sp': 0, + 'sh': 0, + 'ss': 0, + 'sm': 0, + 'sc': 0, + 'sl': 0, + 'se': 0, + 'so': '', + 'su': 0, + }}] ]]):gsub('\n', '')) + end) + + it('works with replacement string items', function() + sd2strings_eq({ + 'Replacement string with timestamp ' .. epoch .. ':', + ' # Unexpected type: map instead of array', + ' = {="a": [10]}', + }, {{type=3, timestamp=0, data={a={10}}}}) + sd2strings_eq({ + 'Replacement string with timestamp ' .. epoch .. ':', + ' @ Description__________ Value', + ' # Expected more elements in list' + }, ([[ [{'type': 3, 'timestamp': 0, 'data': [ + ]}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Replacement string with timestamp ' .. epoch .. ':', + ' @ Description__________ Value', + ' # Expected binary string', + ' - :s replacement string 0', + }, ([[ [{'type': 3, 'timestamp': 0, 'data': [ + 0, + ]}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Replacement string with timestamp ' .. epoch .. ':', + ' @ Description__________ Value', + ' # Expected no NUL bytes', + ' - :s replacement string "abc\\0def"', + }, ([[ [{'type': 3, 'timestamp': 0, 'data': [ + {'_TYPE': v:msgpack_types.binary, '_VAL': ["abc\ndef"]}, + ]}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Replacement string with timestamp ' .. epoch .. ':', + ' @ Description__________ Value', + ' - :s replacement string "abc\\ndef"', + }, ([[ [{'type': 3, 'timestamp': 0, 'data': [ + {'_TYPE': v:msgpack_types.binary, '_VAL': ["abc", "def"]}, + ]}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Replacement string with timestamp ' .. epoch .. ':', + ' @ Description__________ Value', + ' - :s replacement string "abc\\ndef"', + ' - 0', + }, ([[ [{'type': 3, 'timestamp': 0, 'data': [ + {'_TYPE': v:msgpack_types.binary, '_VAL': ["abc", "def"]}, + 0, + ]}] ]]):gsub('\n', '')) + end) + + it('works with history entry items', function() + sd2strings_eq({ + 'History entry with timestamp ' .. epoch .. ':', + ' # Unexpected type: map instead of array', + ' = {="a": [10]}', + }, {{type=4, timestamp=0, data={a={10}}}}) + sd2strings_eq({ + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' # Expected more elements in list' + }, ([[ [{'type': 4, 'timestamp': 0, 'data': [ + ]}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' # Expected integer', + ' - history type ""', + ' # Expected more elements in list' + }, ([[ [{'type': 4, 'timestamp': 0, 'data': [ + '', + ]}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' # Unexpected enum value: expected one of 0 (CMD), 1 (SEARCH), ' + .. '2 (EXPR), 3 (INPUT), 4 (DEBUG)', + ' - history type 5', + ' - contents ""', + }, ([[ [{'type': 4, 'timestamp': 0, 'data': [ + 5, + '' + ]}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' # Unexpected enum value: expected one of 0 (CMD), 1 (SEARCH), ' + .. '2 (EXPR), 3 (INPUT), 4 (DEBUG)', + ' - history type 5', + ' - contents ""', + ' - 32', + }, ([[ [{'type': 4, 'timestamp': 0, 'data': [ + 5, + '', + 0x20 + ]}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type CMD', + ' - contents ""', + ' - 32', + }, ([[ [{'type': 4, 'timestamp': 0, 'data': [ + 0, + '', + 0x20 + ]}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type SEARCH', + ' - contents ""', + ' - separator \' \'', + }, ([[ [{'type': 4, 'timestamp': 0, 'data': [ + 1, + '', + 0x20 + ]}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type SEARCH', + ' - contents ""', + ' # Expected more elements in list', + }, ([[ [{'type': 4, 'timestamp': 0, 'data': [ + 1, + '', + ]}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type EXPR', + ' - contents ""', + }, ([[ [{'type': 4, 'timestamp': 0, 'data': [ + 2, + '', + ]}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type INPUT', + ' - contents ""', + }, ([[ [{'type': 4, 'timestamp': 0, 'data': [ + 3, + '', + ]}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type DEBUG', + ' - contents ""', + }, ([[ [{'type': 4, 'timestamp': 0, 'data': [ + 4, + '', + ]}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type DEBUG', + ' # Expected binary string', + ' - contents 10', + }, ([[ [{'type': 4, 'timestamp': 0, 'data': [ + 4, + 10, + ]}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type DEBUG', + ' # Expected no NUL bytes', + ' - contents "abc\\0def"', + }, ([[ [{'type': 4, 'timestamp': 0, 'data': [ + 4, + {'_TYPE': v:msgpack_types.binary, '_VAL': ["abc\ndef"]}, + ]}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type SEARCH', + ' - contents "abc"', + ' # Expected integer', + ' - separator ""', + }, ([[ [{'type': 4, 'timestamp': 0, 'data': [ + 1, + 'abc', + '', + ]}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type SEARCH', + ' - contents "abc"', + ' # Value is negative', + ' - separator -1', + }, ([[ [{'type': 4, 'timestamp': 0, 'data': [ + 1, + 'abc', + -1, + ]}] ]]):gsub('\n', '')) + end) + + it('works with register items', function() + sd2strings_eq({ + 'Register with timestamp ' .. epoch .. ':', + ' # Unexpected type: array instead of map', + ' = [1, 2, 3]', + }, {{type=5, timestamp=0, data={1, 2, 3}}}) + sd2strings_eq({ + 'Register with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Required key missing: n', + ' # Required key missing: rc', + ' + rw block width 0', + ' + rt type CHARACTERWISE', + }, ([[ [{'type': 5, 'timestamp': 0, 'data': { + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Register with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + n name \' \'', + ' # Required key missing: rc', + ' + rw block width 0', + ' + rt type CHARACTERWISE', + }, ([[ [{'type': 5, 'timestamp': 0, 'data': { + 'n': 0x20, + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Register with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + n name \' \'', + ' + rc contents ["abc", "def"]', + ' + rw block width 0', + ' + rt type CHARACTERWISE', + }, ([[ [{'type': 5, 'timestamp': 0, 'data': { + 'n': 0x20, + 'rc': ["abc", "def"], + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Register with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + n name \' \'', + ' + rc contents @', + ' | - "abcdefghijklmnopqrstuvwxyz"', + ' | - "abcdefghijklmnopqrstuvwxyz"', + ' + rw block width 0', + ' + rt type CHARACTERWISE', + }, ([[ [{'type': 5, 'timestamp': 0, 'data': { + 'n': 0x20, + 'rc': ['abcdefghijklmnopqrstuvwxyz', 'abcdefghijklmnopqrstuvwxyz'], + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Register with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + n name \' \'', + ' + rc contents @', + ' | - "abcdefghijklmnopqrstuvwxyz"', + ' | - "abcdefghijklmnopqrstuvwxyz"', + ' + rw block width 0', + ' + rt type CHARACTERWISE', + }, ([[ [{'type': 5, 'timestamp': 0, 'data': { + 'n': 0x20, + 'rc': ['abcdefghijklmnopqrstuvwxyz', 'abcdefghijklmnopqrstuvwxyz'], + 'rw': 0, + 'rt': 0, + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Register with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + n name \' \'', + ' + rc contents @', + ' | - "abcdefghijklmnopqrstuvwxyz"', + ' | - "abcdefghijklmnopqrstuvwxyz"', + ' + rw block width 5', + ' + rt type LINEWISE', + }, ([[ [{'type': 5, 'timestamp': 0, 'data': { + 'n': 0x20, + 'rc': ['abcdefghijklmnopqrstuvwxyz', 'abcdefghijklmnopqrstuvwxyz'], + 'rw': 5, + 'rt': 1, + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Register with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + n name \' \'', + ' + rc contents @', + ' | - "abcdefghijklmnopqrstuvwxyz"', + ' | - "abcdefghijklmnopqrstuvwxyz"', + ' # Expected integer', + ' + rw block width ""', + ' + rt type BLOCKWISE', + }, ([[ [{'type': 5, 'timestamp': 0, 'data': { + 'n': 0x20, + 'rc': ['abcdefghijklmnopqrstuvwxyz', 'abcdefghijklmnopqrstuvwxyz'], + 'rw': "", + 'rt': 2, + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Register with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + n name \' \'', + ' # Expected array value', + ' + rc contents 0', + ' # Value is negative', + ' + rw block width -1', + ' # Unexpected enum value: expected one of 0 (CHARACTERWISE), ' + .. '1 (LINEWISE), 2 (BLOCKWISE)', + ' + rt type 10', + }, ([[ [{'type': 5, 'timestamp': 0, 'data': { + 'n': 0x20, + 'rc': 0, + 'rw': -1, + 'rt': 10, + }}] ]]):gsub('\n', '')) + end) + + it('works with variable items', function() + sd2strings_eq({ + 'Variable with timestamp ' .. epoch .. ':', + ' # Unexpected type: map instead of array', + ' = {="a": [10]}', + }, {{type=6, timestamp=0, data={a={10}}}}) + sd2strings_eq({ + 'Variable with timestamp ' .. epoch .. ':', + ' @ Description Value', + ' # Expected more elements in list' + }, ([[ [{'type': 6, 'timestamp': 0, 'data': [ + ]}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Variable with timestamp ' .. epoch .. ':', + ' @ Description Value', + ' # Expected binary string', + ' - name 1', + ' # Expected more elements in list', + }, ([[ [{'type': 6, 'timestamp': 0, 'data': [ + 1 + ]}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Variable with timestamp ' .. epoch .. ':', + ' @ Description Value', + ' # Expected no NUL bytes', + ' - name "\\0"', + ' # Expected more elements in list', + }, ([[ [{'type': 6, 'timestamp': 0, 'data': [ + {'_TYPE': v:msgpack_types.binary, '_VAL': ["\n"]}, + ]}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Variable with timestamp ' .. epoch .. ':', + ' @ Description Value', + ' - name "foo"', + ' # Expected more elements in list', + }, ([[ [{'type': 6, 'timestamp': 0, 'data': [ + {'_TYPE': v:msgpack_types.binary, '_VAL': ["foo"]}, + ]}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Variable with timestamp ' .. epoch .. ':', + ' @ Description Value', + ' - name "foo"', + ' - value NIL', + }, ([[ [{'type': 6, 'timestamp': 0, 'data': [ + {'_TYPE': v:msgpack_types.binary, '_VAL': ["foo"]}, + {'_TYPE': v:msgpack_types.nil, '_VAL': ["foo"]}, + ]}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Variable with timestamp ' .. epoch .. ':', + ' @ Description Value', + ' - name "foo"', + ' - value NIL', + ' - NIL', + }, ([[ [{'type': 6, 'timestamp': 0, 'data': [ + {'_TYPE': v:msgpack_types.binary, '_VAL': ["foo"]}, + {'_TYPE': v:msgpack_types.nil, '_VAL': ["foo"]}, + {'_TYPE': v:msgpack_types.nil, '_VAL': ["foo"]}, + ]}] ]]):gsub('\n', '')) + end) + + it('works with global mark items', function() + sd2strings_eq({ + 'Global mark with timestamp ' .. epoch .. ':', + ' # Unexpected type: array instead of map', + ' = [1, 2, 3]', + }, {{type=7, timestamp=0, data={1, 2, 3}}}) + sd2strings_eq({ + 'Global mark with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Required key missing: n', + ' # Required key missing: f', + ' + l line number 1', + ' + c column 0', + }, ([[ [{'type': 7, 'timestamp': 0, 'data': { + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Global mark with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Expected integer', + ' + n name "foo"', + ' # Required key missing: f', + ' + l line number 1', + ' + c column 0', + }, ([[ [{'type': 7, 'timestamp': 0, 'data': { + 'n': 'foo', + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Global mark with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Required key missing: n', + ' + f file name "foo"', + ' + l line number 1', + ' + c column 0', + }, ([[ [{'type': 7, 'timestamp': 0, 'data': { + 'f': 'foo', + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Global mark with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Value is negative', + ' + n name -10', + ' # Expected no NUL bytes', + ' + f file name "\\0"', + ' + l line number 1', + ' + c column 0', + }, ([[ [{'type': 7, 'timestamp': 0, 'data': { + 'n': -10, + 'f': {'_TYPE': v:msgpack_types.binary, '_VAL': ["\n"]}, + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Global mark with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + n name 20', + ' + f file name "foo"', + ' # Value is negative', + ' + l line number -10', + ' # Value is negative', + ' + c column -10', + }, ([[ [{'type': 7, 'timestamp': 0, 'data': { + 'n': 20, + 'f': 'foo', + 'l': -10, + 'c': -10, + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Global mark with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + n name 20', + ' + f file name "foo"', + ' # Expected integer', + ' + l line number "FOO"', + ' # Expected integer', + ' + c column "foo"', + ' + mX 10', + }, ([[ [{'type': 7, 'timestamp': 0, 'data': { + 'n': 20, + 'f': 'foo', + 'l': 'FOO', + 'c': 'foo', + 'mX': 10, + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Global mark with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + ' + f file name "foo"', + ' + l line number 2', + ' + c column 200', + ' + mX 10', + ' + mYYYYYYYYYY 10', + }, ([[ [{'type': 7, 'timestamp': 0, 'data': { + 'n': char2nr('A'), + 'f': 'foo', + 'l': 2, + 'c': 200, + 'mX': 10, + 'mYYYYYYYYYY': 10, + }}] ]]):gsub('\n', '')) + end) + + it('works with jump items', function() + sd2strings_eq({ + 'Jump with timestamp ' .. epoch .. ':', + ' # Unexpected type: array instead of map', + ' = [1, 2, 3]', + }, {{type=8, timestamp=0, data={1, 2, 3}}}) + sd2strings_eq({ + 'Jump with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Required key missing: f', + ' + l line number 1', + ' + c column 0', + }, ([[ [{'type': 8, 'timestamp': 0, 'data': { + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Jump with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Required key missing: f', + ' + l line number 1', + ' + c column 0', + ' # Expected integer', + ' + n name "foo"', + }, ([[ [{'type': 8, 'timestamp': 0, 'data': { + 'n': 'foo', + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Jump with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + f file name "foo"', + ' + l line number 1', + ' + c column 0', + }, ([[ [{'type': 8, 'timestamp': 0, 'data': { + 'f': 'foo', + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Jump with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Expected no NUL bytes', + ' + f file name "\\0"', + ' + l line number 1', + ' + c column 0', + ' # Value is negative', + ' + n name -10', + }, ([[ [{'type': 8, 'timestamp': 0, 'data': { + 'n': -10, + 'f': {'_TYPE': v:msgpack_types.binary, '_VAL': ["\n"]}, + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Jump with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + f file name "foo"', + ' # Value is negative', + ' + l line number -10', + ' # Value is negative', + ' + c column -10', + }, ([[ [{'type': 8, 'timestamp': 0, 'data': { + 'f': 'foo', + 'l': -10, + 'c': -10, + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Jump with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + f file name "foo"', + ' # Expected integer', + ' + l line number "FOO"', + ' # Expected integer', + ' + c column "foo"', + ' + mX 10', + }, ([[ [{'type': 8, 'timestamp': 0, 'data': { + 'f': 'foo', + 'l': 'FOO', + 'c': 'foo', + 'mX': 10, + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Jump with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + f file name "foo"', + ' + l line number 2', + ' + c column 200', + ' + mX 10', + ' + mYYYYYYYYYY 10', + ' + n name \' \'', + }, ([[ [{'type': 8, 'timestamp': 0, 'data': { + 'n': 0x20, + 'f': 'foo', + 'l': 2, + 'c': 200, + 'mX': 10, + 'mYYYYYYYYYY': 10, + }}] ]]):gsub('\n', '')) + end) + + it('works with buffer list items', function() + sd2strings_eq({ + 'Buffer list with timestamp ' .. epoch .. ':', + ' # Unexpected type: map instead of array', + ' = {="a": [10]}', + }, {{type=9, timestamp=0, data={a={10}}}}) + sd2strings_eq({ + 'Buffer list with timestamp ' .. epoch .. ':', + ' # Expected array of maps', + ' = [[], []]', + }, {{type=9, timestamp=0, data={{}, {}}}}) + sd2strings_eq({ + 'Buffer list with timestamp ' .. epoch .. ':', + ' # Expected array of maps', + ' = [{="a": 10}, []]', + }, {{type=9, timestamp=0, data={{a=10}, {}}}}) + sd2strings_eq({ + 'Buffer list with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Required key missing: f', + ' + l line number 1', + ' + c column 0', + ' + a 10', + }, {{type=9, timestamp=0, data={{a=10}}}}) + sd2strings_eq({ + 'Buffer list with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Required key missing: f', + ' # Expected integer', + ' + l line number "10"', + ' # Expected integer', + ' + c column "10"', + ' + a 10', + }, {{type=9, timestamp=0, data={{l='10', c='10', a=10}}}}) + sd2strings_eq({ + 'Buffer list with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Required key missing: f', + ' + l line number 10', + ' + c column 10', + ' + a 10', + }, {{type=9, timestamp=0, data={{l=10, c=10, a=10}}}}) + sd2strings_eq({ + 'Buffer list with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Required key missing: f', + ' # Value is negative', + ' + l line number -10', + ' # Value is negative', + ' + c column -10', + }, {{type=9, timestamp=0, data={{l=-10, c=-10}}}}) + sd2strings_eq({ + 'Buffer list with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + f file name "abc"', + ' + l line number 1', + ' + c column 0', + }, {{type=9, timestamp=0, data={{f='abc'}}}}) + sd2strings_eq({ + 'Buffer list with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Expected binary string', + ' + f file name 10', + ' + l line number 1', + ' + c column 0', + '', + ' % Key Description Value', + ' # Expected binary string', + ' + f file name 20', + ' + l line number 1', + ' + c column 0', + }, {{type=9, timestamp=0, data={{f=10}, {f=20}}}}) + sd2strings_eq({ + 'Buffer list with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Expected binary string', + ' + f file name 10', + ' + l line number 1', + ' + c column 0', + '', + ' % Key Description Value', + ' # Expected no NUL bytes', + ' + f file name "\\0"', + ' + l line number 1', + ' + c column 0', + }, ([[ [{'type': 9, 'timestamp': 0, 'data': [ + {'f': 10}, + {'f': {'_TYPE': v:msgpack_types.binary, '_VAL': ["\n"]}}, + ]}] ]]):gsub('\n', '')) + end) + + it('works with local mark items', function() + sd2strings_eq({ + 'Local mark with timestamp ' .. epoch .. ':', + ' # Unexpected type: array instead of map', + ' = [1, 2, 3]', + }, {{type=10, timestamp=0, data={1, 2, 3}}}) + sd2strings_eq({ + 'Local mark with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Required key missing: f', + ' + n name \'"\'', + ' + l line number 1', + ' + c column 0', + }, ([[ [{'type': 10, 'timestamp': 0, 'data': { + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Local mark with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Required key missing: f', + ' # Expected integer', + ' + n name "foo"', + ' + l line number 1', + ' + c column 0', + }, ([[ [{'type': 10, 'timestamp': 0, 'data': { + 'n': 'foo', + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Local mark with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + f file name "foo"', + ' + n name \'"\'', + ' + l line number 1', + ' + c column 0', + }, ([[ [{'type': 10, 'timestamp': 0, 'data': { + 'f': 'foo', + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Local mark with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Expected no NUL bytes', + ' + f file name "\\0"', + ' # Value is negative', + ' + n name -10', + ' + l line number 1', + ' + c column 0', + }, ([[ [{'type': 10, 'timestamp': 0, 'data': { + 'n': -10, + 'f': {'_TYPE': v:msgpack_types.binary, '_VAL': ["\n"]}, + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Local mark with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + f file name "foo"', + ' + n name 20', + ' # Value is negative', + ' + l line number -10', + ' # Value is negative', + ' + c column -10', + }, ([[ [{'type': 10, 'timestamp': 0, 'data': { + 'n': 20, + 'f': 'foo', + 'l': -10, + 'c': -10, + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Local mark with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + f file name "foo"', + ' + n name 20', + ' # Expected integer', + ' + l line number "FOO"', + ' # Expected integer', + ' + c column "foo"', + ' + mX 10', + }, ([[ [{'type': 10, 'timestamp': 0, 'data': { + 'n': 20, + 'f': 'foo', + 'l': 'FOO', + 'c': 'foo', + 'mX': 10, + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Local mark with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + f file name "foo"', + ' + n name \'a\'', + ' + l line number 2', + ' + c column 200', + ' + mX 10', + ' + mYYYYYYYYYY 10', + }, ([[ [{'type': 10, 'timestamp': 0, 'data': { + 'n': char2nr('a'), + 'f': 'foo', + 'l': 2, + 'c': 200, + 'mX': 10, + 'mYYYYYYYYYY': 10, + }}] ]]):gsub('\n', '')) + end) + + it('works with change items', function() + sd2strings_eq({ + 'Change with timestamp ' .. epoch .. ':', + ' # Unexpected type: array instead of map', + ' = [1, 2, 3]', + }, {{type=11, timestamp=0, data={1, 2, 3}}}) + sd2strings_eq({ + 'Change with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Required key missing: f', + ' + l line number 1', + ' + c column 0', + }, ([[ [{'type': 11, 'timestamp': 0, 'data': { + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Change with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Required key missing: f', + ' + l line number 1', + ' + c column 0', + ' # Expected integer', + ' + n name "foo"', + }, ([[ [{'type': 11, 'timestamp': 0, 'data': { + 'n': 'foo', + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Change with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + f file name "foo"', + ' + l line number 1', + ' + c column 0', + }, ([[ [{'type': 11, 'timestamp': 0, 'data': { + 'f': 'foo', + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Change with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Expected no NUL bytes', + ' + f file name "\\0"', + ' + l line number 1', + ' + c column 0', + ' # Value is negative', + ' + n name -10', + }, ([[ [{'type': 11, 'timestamp': 0, 'data': { + 'n': -10, + 'f': {'_TYPE': v:msgpack_types.binary, '_VAL': ["\n"]}, + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Change with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + f file name "foo"', + ' # Value is negative', + ' + l line number -10', + ' # Value is negative', + ' + c column -10', + }, ([[ [{'type': 11, 'timestamp': 0, 'data': { + 'f': 'foo', + 'l': -10, + 'c': -10, + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Change with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + f file name "foo"', + ' # Expected integer', + ' + l line number "FOO"', + ' # Expected integer', + ' + c column "foo"', + ' + mX 10', + }, ([[ [{'type': 11, 'timestamp': 0, 'data': { + 'f': 'foo', + 'l': 'FOO', + 'c': 'foo', + 'mX': 10, + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Change with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + f file name "foo"', + ' + l line number 2', + ' + c column 200', + ' + mX 10', + ' + mYYYYYYYYYY 10', + ' + n name \' \'', + }, ([[ [{'type': 11, 'timestamp': 0, 'data': { + 'n': 0x20, + 'f': 'foo', + 'l': 2, + 'c': 200, + 'mX': 10, + 'mYYYYYYYYYY': 10, + }}] ]]):gsub('\n', '')) + end) + end) + + describe('function shada#get_strings', function() + it('works', function() + eq({ + 'Header with timestamp ' .. epoch .. ':', + ' % Key Value', + }, nvim_eval('shada#get_strings(msgpackdump([1, 0, 0, {}]))')) + end) + end) + + describe('function shada#strings_to_sd', function() + + local strings2sd_eq = function(expected, input) + nvim('set_var', '__input', input) + nvim_command('let g:__actual = map(shada#strings_to_sd(g:__input), ' + .. '"filter(v:val, \\"v:key[0] isnot# \'_\' ' + .. '&& v:key isnot# \'length\'\\")")') + -- print() + if type(expected) == 'table' then + nvim('set_var', '__expected', expected) + nvim_command('let g:__expected = ModifyVal(g:__expected)') + expected = 'g:__expected' + -- print(nvim_eval('msgpack#string(g:__expected)')) + end + -- print(nvim_eval('msgpack#string(g:__actual)')) + eq(1, nvim_eval(('msgpack#equal(%s, g:__actual)'):format(expected))) + if type(expected) == 'table' then + nvim_command('unlet g:__expected') + end + nvim_command('unlet g:__input') + nvim_command('unlet g:__actual') + end + + assert:set_parameter('TableFormatLevel', 100) + + it('works with multiple items', function() + strings2sd_eq({{ + type=11, timestamp=0, data={ + f='foo', + l=2, + c=200, + mX=10, + mYYYYYYYYYY=10, + n=(' '):byte(), + } + }, { + type=1, timestamp=0, data={ + c='abc', + f={'!binary', {'abc\ndef'}}, + l=-10, + n=-64, + rc='10', + rt=10, + sc={'!nil', 0}, + sm='TRUE', + so='TRUE', + sp={'!string', {'abc'}}, + } + }}, { + 'Change with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + f file name "foo"', + ' + l line number 2', + ' + c column 200', + ' + mX 10', + ' + mYYYYYYYYYY 10', + ' + n name \' \'', + 'Header with timestamp ' .. epoch .. ':', + ' % Key Description____ Value', + ' # Expected integer', + ' + c column "abc"', + ' # Expected no NUL bytes', + ' + f file name "abc\\0def"', + ' # Value is negative', + ' + l line number -10', + ' # Value is negative', + ' + n name -64', + ' # Expected array value', + ' + rc contents "10"', + ' # Unexpected enum value: expected one of ' + .. '0 (CHARACTERWISE), 1 (LINEWISE), 2 (BLOCKWISE)', + ' + rt type 10', + ' # Expected boolean', + ' + sc smartcase value NIL', + ' # Expected boolean', + ' + sm magic value "TRUE"', + ' # Expected integer', + ' + so offset value "TRUE"', + ' # Expected binary string', + ' + sp pattern ="abc"', + }) + end) + + it('works with empty list', function() + strings2sd_eq({}, {}) + end) + + it('works with header items', function() + strings2sd_eq({{type=1, timestamp=0, data={ + generator='test', + }}}, { + 'Header with timestamp ' .. epoch .. ':', + ' % Key______ Value', + ' + generator "test"', + }) + strings2sd_eq({{type=1, timestamp=0, data={ + 1, 2, 3, + }}}, { + 'Header with timestamp ' .. epoch .. ':', + ' # Unexpected type: array instead of map', + ' = [1, 2, 3]', + }) + strings2sd_eq({{type=1, timestamp=0, data={ + a=1, b=2, c=3, d=4, + }}}, { + 'Header with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + a 1', + ' + b 2', + ' + c column 3', + ' + d 4', + }) + strings2sd_eq({{type=1, timestamp=0, data={ + c='abc', + f={'!binary', {'abc\ndef'}}, + l=-10, + n=-64, + rc='10', + rt=10, + sc={'!nil', 0}, + sm='TRUE', + so='TRUE', + sp={'!string', {'abc'}}, + }}}, { + 'Header with timestamp ' .. epoch .. ':', + ' % Key Description____ Value', + ' # Expected integer', + ' + c column "abc"', + ' # Expected no NUL bytes', + ' + f file name "abc\\0def"', + ' # Value is negative', + ' + l line number -10', + ' # Value is negative', + ' + n name -64', + ' # Expected array value', + ' + rc contents "10"', + ' # Unexpected enum value: expected one of ' + .. '0 (CHARACTERWISE), 1 (LINEWISE), 2 (BLOCKWISE)', + ' + rt type 10', + ' # Expected boolean', + ' + sc smartcase value NIL', + ' # Expected boolean', + ' + sm magic value "TRUE"', + ' # Expected integer', + ' + so offset value "TRUE"', + ' # Expected binary string', + ' + sp pattern ="abc"', + }) + end) + + it('works with search pattern items', function() + strings2sd_eq({{type=2, timestamp=0, data={ + 1, 2, 3 + }}}, { + 'Search pattern with timestamp ' .. epoch .. ':', + ' # Unexpected type: array instead of map', + ' = [1, 2, 3]', + }) + strings2sd_eq({{type=2, timestamp=0, data={ + sp='abc', + }}}, { + 'Search pattern with timestamp ' .. epoch .. ':', + ' % Key Description________ Value', + ' + sp pattern "abc"', + ' + sh v:hlsearch value FALSE', + ' + ss is :s pattern FALSE', + ' + sm magic value TRUE', + ' + sc smartcase value FALSE', + ' + sl has line offset FALSE', + ' + se place cursor at end FALSE', + ' + so offset value 0', + ' + su is last used TRUE', + }) + strings2sd_eq({{type=2, timestamp=0, data={ + sp='abc', + sX={'!nil', 0}, + sY={'!nil', 0}, + sZ={'!nil', 0}, + }}}, { + 'Search pattern with timestamp ' .. epoch .. ':', + ' % Key Description________ Value', + ' + sp pattern "abc"', + ' + sh v:hlsearch value FALSE', + ' + ss is :s pattern FALSE', + ' + sm magic value TRUE', + ' + sc smartcase value FALSE', + ' + sl has line offset FALSE', + ' + se place cursor at end FALSE', + ' + so offset value 0', + ' + su is last used TRUE', + ' + sX NIL', + ' + sY NIL', + ' + sZ NIL', + }) + strings2sd_eq({{type=2, timestamp=0, data={'!map', { + }}}}, { + 'Search pattern with timestamp ' .. epoch .. ':', + ' % Key Description________ Value', + ' # Required key missing: sp', + ' + sh v:hlsearch value FALSE', + ' + ss is :s pattern FALSE', + ' + sm magic value TRUE', + ' + sc smartcase value FALSE', + ' + sl has line offset FALSE', + ' + se place cursor at end FALSE', + ' + so offset value 0', + ' + su is last used TRUE', + }) + strings2sd_eq({{type=2, timestamp=0, data={ + sp='', + sh={'!boolean', 1}, + ss={'!boolean', 1}, + sc={'!boolean', 1}, + sl={'!boolean', 1}, + se={'!boolean', 1}, + sm={'!boolean', 0}, + su={'!boolean', 0}, + so=-10, + }}}, { + 'Search pattern with timestamp ' .. epoch .. ':', + ' % Key Description________ Value', + ' + sp pattern ""', + ' + sh v:hlsearch value TRUE', + ' + ss is :s pattern TRUE', + ' + sm magic value FALSE', + ' + sc smartcase value TRUE', + ' + sl has line offset TRUE', + ' + se place cursor at end TRUE', + ' + so offset value -10', + ' + su is last used FALSE', + }) + strings2sd_eq({{type=2, timestamp=0, data={ + sp=0, + sh=0, + ss=0, + sc=0, + sl=0, + se=0, + sm=0, + su=0, + so='', + }}}, { + 'Search pattern with timestamp ' .. epoch .. ':', + ' % Key Description________ Value', + ' # Expected binary string', + ' + sp pattern 0', + ' # Expected boolean', + ' + sh v:hlsearch value 0', + ' # Expected boolean', + ' + ss is :s pattern 0', + ' # Expected boolean', + ' + sm magic value 0', + ' # Expected boolean', + ' + sc smartcase value 0', + ' # Expected boolean', + ' + sl has line offset 0', + ' # Expected boolean', + ' + se place cursor at end 0', + ' # Expected integer', + ' + so offset value ""', + ' # Expected boolean', + ' + su is last used 0', + }) + end) + + it('works with replacement string items', function() + strings2sd_eq({{type=3, timestamp=0, data={ + a={10} + }}}, { + 'Replacement string with timestamp ' .. epoch .. ':', + ' # Unexpected type: map instead of array', + ' = {="a": [10]}', + }) + strings2sd_eq({{type=3, timestamp=0, data={ + }}}, { + 'Replacement string with timestamp ' .. epoch .. ':', + ' @ Description__________ Value', + ' # Expected more elements in list' + }) + strings2sd_eq({{type=3, timestamp=0, data={ + 0 + }}}, { + 'Replacement string with timestamp ' .. epoch .. ':', + ' @ Description__________ Value', + ' # Expected binary string', + ' - :s replacement string 0', + }) + strings2sd_eq({{type=3, timestamp=0, data={ + 'abc\ndef', 0, + }}}, { + 'Replacement string with timestamp ' .. epoch .. ':', + ' @ Description__________ Value', + ' - :s replacement string "abc\\ndef"', + ' - 0', + }) + strings2sd_eq({{type=3, timestamp=0, data={ + 'abc\ndef', + }}}, { + 'Replacement string with timestamp ' .. epoch .. ':', + ' @ Description__________ Value', + ' - :s replacement string "abc\\ndef"', + }) + end) + + it('works with history entry items', function() + strings2sd_eq({{type=4, timestamp=0, data={ + a={10}, + }}}, { + 'History entry with timestamp ' .. epoch .. ':', + ' # Unexpected type: map instead of array', + ' = {="a": [10]}', + }) + strings2sd_eq({{type=4, timestamp=0, data={ + }}}, { + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' # Expected more elements in list' + }) + strings2sd_eq({{type=4, timestamp=0, data={ + '', + }}}, { + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' # Expected integer', + ' - history type ""', + ' # Expected more elements in list' + }) + strings2sd_eq({{type=4, timestamp=0, data={ + 5, '', + }}}, { + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' # Unexpected enum value: expected one of 0 (CMD), 1 (SEARCH), ' + .. '2 (EXPR), 3 (INPUT), 4 (DEBUG)', + ' - history type 5', + ' - contents ""', + }) + strings2sd_eq({{type=4, timestamp=0, data={ + 5, '', 32, + }}}, { + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' # Unexpected enum value: expected one of 0 (CMD), 1 (SEARCH), ' + .. '2 (EXPR), 3 (INPUT), 4 (DEBUG)', + ' - history type 5', + ' - contents ""', + ' - 32', + }) + strings2sd_eq({{type=4, timestamp=0, data={ + 0, '', 32, + }}}, { + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type CMD', + ' - contents ""', + ' - 32', + }) + strings2sd_eq({{type=4, timestamp=0, data={ + 1, '', 32, + }}}, { + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type SEARCH', + ' - contents ""', + ' - separator \' \'', + }) + strings2sd_eq({{type=4, timestamp=0, data={ + 1, '', + }}}, { + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type SEARCH', + ' - contents ""', + ' # Expected more elements in list', + }) + strings2sd_eq({{type=4, timestamp=0, data={ + 2, '', + }}}, { + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type EXPR', + ' - contents ""', + }) + strings2sd_eq({{type=4, timestamp=0, data={ + 3, '' + }}}, { + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type INPUT', + ' - contents ""', + }) + strings2sd_eq({{type=4, timestamp=0, data={ + 4, '' + }}}, { + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type DEBUG', + ' - contents ""', + }) + end) + + it('works with register items', function() + strings2sd_eq({{type=5, timestamp=0, data={ + 1, 2, 3 + }}}, { + 'Register with timestamp ' .. epoch .. ':', + ' # Unexpected type: array instead of map', + ' = [1, 2, 3]', + }) + strings2sd_eq({{type=5, timestamp=0, data={'!map', { + }}}}, { + 'Register with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Required key missing: n', + ' # Required key missing: rc', + ' + rw block width 0', + ' + rt type CHARACTERWISE', + }) + strings2sd_eq({{type=5, timestamp=0, data={ + n=(' '):byte() + }}}, { + 'Register with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + n name \' \'', + ' # Required key missing: rc', + ' + rw block width 0', + ' + rt type CHARACTERWISE', + }) + strings2sd_eq({{type=5, timestamp=0, data={ + n=(' '):byte(), rc={'abc', 'def'} + }}}, { + 'Register with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + n name \' \'', + ' + rc contents ["abc", "def"]', + ' + rw block width 0', + ' + rt type CHARACTERWISE', + }) + strings2sd_eq({{type=5, timestamp=0, data={ + n=(' '):byte(), + rc={'abcdefghijklmnopqrstuvwxyz', 'abcdefghijklmnopqrstuvwxyz'}, + }}}, { + 'Register with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + n name \' \'', + ' + rc contents @', + ' | - "abcdefghijklmnopqrstuvwxyz"', + ' | - "abcdefghijklmnopqrstuvwxyz"', + ' + rw block width 0', + ' + rt type CHARACTERWISE', + }) + strings2sd_eq({{type=5, timestamp=0, data={ + n=(' '):byte(), + rc={'abcdefghijklmnopqrstuvwxyz', 'abcdefghijklmnopqrstuvwxyz'}, + rw=5, + rt=1, + }}}, { + 'Register with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + n name \' \'', + ' + rc contents @', + ' | - "abcdefghijklmnopqrstuvwxyz"', + ' | - "abcdefghijklmnopqrstuvwxyz"', + ' + rw block width 5', + ' + rt type LINEWISE', + }) + strings2sd_eq({{type=5, timestamp=0, data={ + n=(' '):byte(), + rc={'abcdefghijklmnopqrstuvwxyz', 'abcdefghijklmnopqrstuvwxyz'}, + rw=5, + rt=2, + }}}, { + 'Register with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + n name \' \'', + ' + rc contents @', + ' | - "abcdefghijklmnopqrstuvwxyz"', + ' | - "abcdefghijklmnopqrstuvwxyz"', + ' + rw block width 5', + ' + rt type BLOCKWISE', + }) + strings2sd_eq({{type=5, timestamp=0, data={ + n=(' '):byte(), + rc=0, + rw=-1, + rt=10, + }}}, { + 'Register with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + n name \' \'', + ' # Expected array value', + ' + rc contents 0', + ' # Value is negative', + ' + rw block width -1', + ' # Unexpected enum value: expected one of 0 (CHARACTERWISE), ' + .. '1 (LINEWISE), 2 (BLOCKWISE)', + ' + rt type 10', + }) + end) + + it('works with variable items', function() + strings2sd_eq({{type=6, timestamp=0, data={ + a={10} + }}}, { + 'Variable with timestamp ' .. epoch .. ':', + ' # Unexpected type: map instead of array', + ' = {="a": [10]}', + }) + strings2sd_eq({{type=6, timestamp=0, data={ + }}}, { + 'Variable with timestamp ' .. epoch .. ':', + ' @ Description Value', + ' # Expected more elements in list' + }) + strings2sd_eq({{type=6, timestamp=0, data={ + 'foo', + }}}, { + 'Variable with timestamp ' .. epoch .. ':', + ' @ Description Value', + ' - name "foo"', + ' # Expected more elements in list', + }) + strings2sd_eq({{type=6, timestamp=0, data={ + 'foo', {'!nil', 0}, + }}}, { + 'Variable with timestamp ' .. epoch .. ':', + ' @ Description Value', + ' - name "foo"', + ' - value NIL', + }) + strings2sd_eq({{type=6, timestamp=0, data={ + 'foo', {'!nil', 0}, {'!nil', 0} + }}}, { + 'Variable with timestamp ' .. epoch .. ':', + ' @ Description Value', + ' - name "foo"', + ' - value NIL', + ' - NIL', + }) + end) + + it('works with global mark items', function() + strings2sd_eq({{type=7, timestamp=0, data={ + 1, 2, 3 + }}}, { + 'Global mark with timestamp ' .. epoch .. ':', + ' # Unexpected type: array instead of map', + ' = [1, 2, 3]', + }) + strings2sd_eq({{type=7, timestamp=0, data={ + n=('A'):byte(), f='foo', l=2, c=200, mX=10, mYYYYYYYYYY=10, + }}}, { + 'Global mark with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + ' + f file name "foo"', + ' + l line number 2', + ' + c column 200', + ' + mX 10', + ' + mYYYYYYYYYY 10', + }) + end) + + it('works with jump items', function() + strings2sd_eq({{type=8, timestamp=0, data={ + 1, 2, 3 + }}}, { + 'Jump with timestamp ' .. epoch .. ':', + ' # Unexpected type: array instead of map', + ' = [1, 2, 3]', + }) + strings2sd_eq({{type=8, timestamp=0, data={ + n=('A'):byte(), f='foo', l=2, c=200, mX=10, mYYYYYYYYYY=10, + }}}, { + 'Jump with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + ' + f file name "foo"', + ' + l line number 2', + ' + c column 200', + ' + mX 10', + ' + mYYYYYYYYYY 10', + }) + end) + + it('works with buffer list items', function() + strings2sd_eq({{type=9, timestamp=0, data={ + a={10} + }}}, { + 'Buffer list with timestamp ' .. epoch .. ':', + ' # Unexpected type: map instead of array', + ' = {="a": [10]}', + }) + strings2sd_eq({{type=9, timestamp=0, data={ + {a=10}, {} + }}}, { + 'Buffer list with timestamp ' .. epoch .. ':', + ' # Expected array of maps', + ' = [{="a": 10}, []]', + }) + strings2sd_eq({{type=9, timestamp=0, data={ + {a=10}, + }}}, { + 'Buffer list with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Required key missing: f', + ' + l line number 1', + ' + c column 0', + ' + a 10', + }) + strings2sd_eq({{type=9, timestamp=0, data={ + {l='10', c='10', a=10}, + }}}, { + 'Buffer list with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Required key missing: f', + ' # Expected integer', + ' + l line number "10"', + ' # Expected integer', + ' + c column "10"', + ' + a 10', + }) + strings2sd_eq({{type=9, timestamp=0, data={ + {l=10, c=10, a=10}, + }}}, { + 'Buffer list with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Required key missing: f', + ' + l line number 10', + ' + c column 10', + ' + a 10', + }) + strings2sd_eq({{type=9, timestamp=0, data={ + {l=-10, c=-10}, + }}}, { + 'Buffer list with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Required key missing: f', + ' # Value is negative', + ' + l line number -10', + ' # Value is negative', + ' + c column -10', + }) + strings2sd_eq({{type=9, timestamp=0, data={ + {f='abc'}, + }}}, { + 'Buffer list with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + f file name "abc"', + ' + l line number 1', + ' + c column 0', + }) + strings2sd_eq({{type=9, timestamp=0, data={ + {f=10}, {f=20}, + }}}, { + 'Buffer list with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Expected binary string', + ' + f file name 10', + ' + l line number 1', + ' + c column 0', + '', + ' % Key Description Value', + ' # Expected binary string', + ' + f file name 20', + ' + l line number 1', + ' + c column 0', + }) + strings2sd_eq({{type=9, timestamp=0, data={ + {f=10}, {f={'!binary', {'\n'}}}, + }}}, { + 'Buffer list with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Expected binary string', + ' + f file name 10', + ' + l line number 1', + ' + c column 0', + '', + ' % Key Description Value', + ' # Expected no NUL bytes', + ' + f file name "\\0"', + ' + l line number 1', + ' + c column 0', + }) + end) + + it('works with local mark items', function() + strings2sd_eq({{type=10, timestamp=0, data={ + 1, 2, 3 + }}}, { + 'Local mark with timestamp ' .. epoch .. ':', + ' # Unexpected type: array instead of map', + ' = [1, 2, 3]', + }) + strings2sd_eq({{type=10, timestamp=0, data={ + n=('A'):byte(), f='foo', l=2, c=200, mX=10, mYYYYYYYYYY=10, + }}}, { + 'Local mark with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + ' + f file name "foo"', + ' + l line number 2', + ' + c column 200', + ' + mX 10', + ' + mYYYYYYYYYY 10', + }) + end) + + it('works with change items', function() + strings2sd_eq({{type=11, timestamp=0, data={ + 1, 2, 3 + }}}, { + 'Change with timestamp ' .. epoch .. ':', + ' # Unexpected type: array instead of map', + ' = [1, 2, 3]', + }) + strings2sd_eq({{type=11, timestamp=0, data={ + n=('A'):byte(), f='foo', l=2, c=200, mX=10, mYYYYYYYYYY=10, + }}}, { + 'Change with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + ' + f file name "foo"', + ' + l line number 2', + ' + c column 200', + ' + mX 10', + ' + mYYYYYYYYYY 10', + }) + end) + end) + + describe('function shada#get_binstrings', function() + local getbstrings_eq = function(expected, input) + local result = funcs['shada#get_binstrings'](input) + for i, s in ipairs(result) do + result[i] = s:gsub('\n', '\0') + end + local mpack_result = table.concat(result, '\n') + return mpack_eq(expected, mpack_result) + end + + it('works', function() + getbstrings_eq({{timestamp='current', type=1, value={ + generator='shada.vim', + version=704, + }}}, {}) + getbstrings_eq({ + {timestamp='current', type=1, value={ + generator='shada.vim', version=704 + }}, + {timestamp=0, type=1, value={generator='test'}} + }, { + 'Header with timestamp ' .. epoch .. ':', + ' % Key______ Value', + ' + generator "test"', + }) + nvim('set_var', 'shada#add_own_header', 1) + getbstrings_eq({{timestamp='current', type=1, value={ + generator='shada.vim', + version=704, + }}}, {}) + getbstrings_eq({ + {timestamp='current', type=1, value={ + generator='shada.vim', version=704 + }}, + {timestamp=0, type=1, value={generator='test'}} + }, { + 'Header with timestamp ' .. epoch .. ':', + ' % Key______ Value', + ' + generator "test"', + }) + nvim('set_var', 'shada#add_own_header', 0) + getbstrings_eq({}, {}) + getbstrings_eq({{timestamp=0, type=1, value={generator='test'}}}, { + 'Header with timestamp ' .. epoch .. ':', + ' % Key______ Value', + ' + generator "test"', + }) + nvim('set_var', 'shada#keep_old_header', 0) + getbstrings_eq({}, { + 'Header with timestamp ' .. epoch .. ':', + ' % Key______ Value', + ' + generator "test"', + }) + getbstrings_eq({ + {type=3, timestamp=0, value={'abc\ndef'}}, + {type=3, timestamp=0, value={'abc\ndef'}}, + {type=3, timestamp=0, value={'abc\ndef'}}, + }, { + 'Replacement string with timestamp ' .. epoch .. ':', + ' @ Description__________ Value', + ' - :s replacement string "abc\\ndef"', + 'Replacement string with timestamp ' .. epoch .. ':', + ' @ Description__________ Value', + ' - :s replacement string "abc\\ndef"', + 'Replacement string with timestamp ' .. epoch .. ':', + ' @ Description__________ Value', + ' - :s replacement string "abc\\ndef"', + }) + end) + end) +end) + +describe('In plugin/shada.vim', function() + local epoch = os.date('%Y-%m-%dT%H:%M:%S', 0) + before_each(function() + reset() + os.remove(fname) + os.remove(fname .. '.tst') + os.remove(fname_tmp) + end) + + local shada_eq = function(expected, fname) + local fd = io.open(fname) + local mpack_result = fd:read('*a') + fd:close() + mpack_eq(expected, mpack_result) + end + + describe('event BufReadCmd', function() + it('works', function() + wshada('\004\000\009\147\000\196\002ab\196\001a') + wshada_tmp('\004\000\009\147\000\196\002ab\196\001b') + nvim_command('edit ' .. fname) + eq({ + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type CMD', + ' - contents "ab"', + ' - "a"', + }, nvim_eval('getline(1, "$")')) + eq(false, curbuf('get_option', 'modified')) + eq('shada', curbuf('get_option', 'filetype')) + nvim_command('edit ' .. fname_tmp) + eq({ + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type CMD', + ' - contents "ab"', + ' - "b"', + }, nvim_eval('getline(1, "$")')) + eq(false, curbuf('get_option', 'modified')) + eq('shada', curbuf('get_option', 'filetype')) + eq('++opt not supported', exc_exec('edit ++enc=latin1 ' .. fname)) + neq({ + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type CMD', + ' - contents "ab"', + ' - "a"', + }, nvim_eval('getline(1, "$")')) + neq(true, curbuf('get_option', 'modified')) + end) + end) + + describe('event FileReadCmd', function() + it('works', function() + wshada('\004\000\009\147\000\196\002ab\196\001a') + wshada_tmp('\004\000\009\147\000\196\002ab\196\001b') + nvim_command('$read ' .. fname) + eq({ + '', + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type CMD', + ' - contents "ab"', + ' - "a"', + }, nvim_eval('getline(1, "$")')) + eq(true, curbuf('get_option', 'modified')) + neq('shada', curbuf('get_option', 'filetype')) + nvim_command('1,$read ' .. fname_tmp) + eq({ + '', + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type CMD', + ' - contents "ab"', + ' - "a"', + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type CMD', + ' - contents "ab"', + ' - "b"', + }, nvim_eval('getline(1, "$")')) + eq(true, curbuf('get_option', 'modified')) + neq('shada', curbuf('get_option', 'filetype')) + curbuf('set_option', 'modified', false) + eq('++opt not supported', exc_exec('$read ++enc=latin1 ' .. fname)) + eq({ + '', + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type CMD', + ' - contents "ab"', + ' - "a"', + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type CMD', + ' - contents "ab"', + ' - "b"', + }, nvim_eval('getline(1, "$")')) + neq(true, curbuf('get_option', 'modified')) + end) + end) + + describe('event BufWriteCmd', function() + it('works', function() + nvim('set_var', 'shada#add_own_header', 0) + curbuf('set_line_slice', 0, 0, true, true, { + 'Jump with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + ' + f file name ["foo"]', + ' + l line number 2', + ' + c column -200', + 'Jump with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + ' + f file name ["foo"]', + ' + l line number 2', + ' + c column -200', + }) + nvim_command('w ' .. fname .. '.tst') + nvim_command('w ' .. fname) + nvim_command('w ' .. fname_tmp) + eq('++opt not supported', exc_exec('w! ++enc=latin1 ' .. fname)) + eq(table.concat({ + 'Jump with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + ' + f file name ["foo"]', + ' + l line number 2', + ' + c column -200', + 'Jump with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + ' + f file name ["foo"]', + ' + l line number 2', + ' + c column -200', + }, '\n') .. '\n', io.open(fname .. '.tst'):read('*a')) + shada_eq({{ + timestamp=0, + type=8, + value={c=-200, f={'foo'}, l=2, n=('A'):byte()}, + }, { + timestamp=0, + type=8, + value={c=-200, f={'foo'}, l=2, n=('A'):byte()}, + }}, fname) + shada_eq({{ + timestamp=0, + type=8, + value={c=-200, f={'foo'}, l=2, n=('A'):byte()}, + }, { + timestamp=0, + type=8, + value={c=-200, f={'foo'}, l=2, n=('A'):byte()}, + }}, fname_tmp) + end) + end) + + describe('event FileWriteCmd', function() + it('works', function() + nvim('set_var', 'shada#add_own_header', 0) + curbuf('set_line_slice', 0, 0, true, true, { + 'Jump with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + ' + f file name ["foo"]', + ' + l line number 2', + ' + c column -200', + 'Jump with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + ' + f file name ["foo"]', + ' + l line number 2', + ' + c column -200', + }) + nvim_command('1,3w ' .. fname .. '.tst') + nvim_command('1,3w ' .. fname) + nvim_command('1,3w ' .. fname_tmp) + eq('++opt not supported', exc_exec('1,3w! ++enc=latin1 ' .. fname)) + eq(table.concat({ + 'Jump with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + }, '\n') .. '\n', io.open(fname .. '.tst'):read('*a')) + shada_eq({{ + timestamp=0, + type=8, + value={n=('A'):byte()}, + }}, fname) + shada_eq({{ + timestamp=0, + type=8, + value={n=('A'):byte()}, + }}, fname_tmp) + end) + end) + + describe('event FileAppendCmd', function() + it('works', function() + nvim('set_var', 'shada#add_own_header', 0) + curbuf('set_line_slice', 0, 0, true, true, { + 'Jump with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + ' + f file name ["foo"]', + ' + l line number 2', + ' + c column -200', + 'Jump with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + ' + f file name ["foo"]', + ' + l line number 2', + ' + c column -200', + }) + funcs.writefile({''}, fname .. '.tst', 'b') + funcs.writefile({''}, fname, 'b') + funcs.writefile({''}, fname_tmp, 'b') + nvim_command('1,3w >> ' .. fname .. '.tst') + nvim_command('1,3w >> ' .. fname) + nvim_command('1,3w >> ' .. fname_tmp) + nvim_command('w >> ' .. fname .. '.tst') + nvim_command('w >> ' .. fname) + nvim_command('w >> ' .. fname_tmp) + eq('++opt not supported', exc_exec('1,3w! ++enc=latin1 >> ' .. fname)) + eq(table.concat({ + 'Jump with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + 'Jump with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + ' + f file name ["foo"]', + ' + l line number 2', + ' + c column -200', + 'Jump with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + ' + f file name ["foo"]', + ' + l line number 2', + ' + c column -200', + }, '\n') .. '\n', io.open(fname .. '.tst'):read('*a')) + shada_eq({{ + timestamp=0, + type=8, + value={n=('A'):byte()}, + }, { + timestamp=0, + type=8, + value={c=-200, f={'foo'}, l=2, n=('A'):byte()}, + }, { + timestamp=0, + type=8, + value={c=-200, f={'foo'}, l=2, n=('A'):byte()}, + }}, fname) + shada_eq({{ + timestamp=0, + type=8, + value={n=('A'):byte()}, + }, { + timestamp=0, + type=8, + value={c=-200, f={'foo'}, l=2, n=('A'):byte()}, + }, { + timestamp=0, + type=8, + value={c=-200, f={'foo'}, l=2, n=('A'):byte()}, + }}, fname_tmp) + end) + end) + + describe('event SourceCmd', function() + before_each(function() + reset(fname) + end) + it('works', function() + wshada('\004\000\006\146\000\196\002ab') + wshada_tmp('\004\001\006\146\000\196\002bc') + eq(0, exc_exec('source ' .. fname)) + eq(0, exc_exec('source ' .. fname_tmp)) + eq('bc', funcs.histget(':', -1)) + eq('ab', funcs.histget(':', -2)) + end) + end) +end) + +describe('ftplugin/shada.vim', function() + local epoch = os.date('%Y-%m-%dT%H:%M:%S', 0) + before_each(reset) + + it('sets indentexpr correctly', function() + nvim_command('filetype plugin indent on') + nvim_command('setlocal filetype=shada') + funcs.setline(1, { + 'Jump with timestamp ' .. epoch .. ':', + '% Key________ Description Value', + '+ n name \'A\'', + '+ f file name "foo"', + '+ l line number 2', + '+ c column 200', + '+ mX 10', + '+ mYYYYYYYYYY 10', + 'Register with timestamp ' .. epoch .. ':', + '% Key Description Value', + '+ n name \' \'', + '+ rc contents @', + '| - "abcdefghijklmnopqrstuvwxyz"', + '| - "abcdefghijklmnopqrstuvwxyz"', + '+ rw block width 0', + '+ rt type CHARACTERWISE', + 'Replacement string with timestamp ' .. epoch .. ':', + ' @ Description__________ Value', + ' - :s replacement string "abc\\ndef"', + ' Buffer list with timestamp ' .. epoch .. ':', + ' # Expected array of maps', + '= [{="a": 10}, []]', + ' Buffer list with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Expected binary string', + '+ f file name 10', + ' + l line number 1', + ' + c column 0', + '', + ' % Key Description Value', + ' # Expected binary string', + ' + f file name 20', + '+ l line number 1', + ' + c column 0', + }) + nvim_command('normal! gg=G') + eq({ + 'Jump with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + ' + f file name "foo"', + ' + l line number 2', + ' + c column 200', + ' + mX 10', + ' + mYYYYYYYYYY 10', + 'Register with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + n name \' \'', + ' + rc contents @', + ' | - "abcdefghijklmnopqrstuvwxyz"', + ' | - "abcdefghijklmnopqrstuvwxyz"', + ' + rw block width 0', + ' + rt type CHARACTERWISE', + 'Replacement string with timestamp ' .. epoch .. ':', + ' @ Description__________ Value', + ' - :s replacement string "abc\\ndef"', + 'Buffer list with timestamp ' .. epoch .. ':', + ' # Expected array of maps', + ' = [{="a": 10}, []]', + 'Buffer list with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' # Expected binary string', + ' + f file name 10', + ' + l line number 1', + ' + c column 0', + '', + ' % Key Description Value', + ' # Expected binary string', + ' + f file name 20', + ' + l line number 1', + ' + c column 0', + }, funcs.getline(1, funcs.line('$'))) + end) + + it('sets options correctly', function() + nvim_command('filetype plugin indent on') + nvim_command('setlocal filetype=shada') + eq(true, curbuf('get_option', 'expandtab')) + eq(2, curbuf('get_option', 'tabstop')) + eq(2, curbuf('get_option', 'softtabstop')) + eq(2, curbuf('get_option', 'shiftwidth')) + end) + + it('sets indentkeys correctly', function() + nvim_command('filetype plugin indent on') + nvim_command('setlocal filetype=shada') + funcs.setline(1, ' Replacement with timestamp ' .. epoch) + nvim_feed('ggA:\027') + eq('Replacement with timestamp ' .. epoch .. ':', curbuf('get_line', 0)) + nvim_feed('o-\027') + eq(' -', curbuf('get_line', 1)) + nvim_feed('ggO+\027') + eq('+', curbuf('get_line', 0)) + nvim_feed('GO*\027') + eq(' *', curbuf('get_line', 2)) + nvim_feed('ggO /\027') + eq(' /', curbuf('get_line', 0)) + nvim_feed('ggOx\027') + eq('x', curbuf('get_line', 0)) + end) +end) + +describe('syntax/shada.vim', function() + local epoch = os.date('%Y-%m-%dT%H:%M:%S', 0) + before_each(reset) + + it('works', function() + nvim_command('syntax on') + nvim_command('setlocal syntax=shada') + curbuf('set_line_slice', 0, 0, true, true, { + 'Header with timestamp ' .. epoch .. ':', + ' % Key Value', + ' + t "test"', + 'Jump with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + ' + f file name ["foo"]', + ' + l line number 2', + ' + c column -200', + 'Register with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + rc contents @', + ' | - {"abcdefghijklmnopqrstuvwxyz": 1.0}', + ' + rt type CHARACTERWISE', + ' + rt type LINEWISE', + ' + rt type BLOCKWISE', + 'Replacement string with timestamp ' .. epoch .. ':', + ' @ Description__________ Value', + ' - :s replacement string CMD', + ' - :s replacement string SEARCH', + ' - :s replacement string EXPR', + ' - :s replacement string INPUT', + ' - :s replacement string DEBUG', + 'Buffer list with timestamp ' .. epoch .. ':', + ' # Expected array of maps', + ' = [{="a": +(10)"ac\\0df\\ngi\\"tt\\.", TRUE: FALSE}, [NIL, +(-10)""]]', + 'Buffer list with timestamp ' .. epoch .. ':', + ' % Key Description Value', + '', + ' % Key Description Value', + 'Header with timestamp ' .. epoch .. ':', + ' % Key Description________ Value', + ' + se place cursor at end TRUE', + }) + nvim_command([[ + function GetSyntax() + let lines = [] + for l in range(1, line('$')) + let columns = [] + let line = getline(l) + for c in range(1, col([l, '$']) - 1) + let synstack = map(synstack(l, c), 'synIDattr(v:val, "name")') + if !empty(columns) && columns[-1][0] ==# synstack + let columns[-1][1] .= line[c - 1] + else + call add(columns, [ synstack, line[c - 1] ]) + endif + endfor + call add(lines, columns) + endfor + return lines + endfunction + ]]) + local hname = function(s) return {{'ShaDaEntryHeader', 'ShaDaEntryName'}, + s} end + local h = function(s) return {{'ShaDaEntryHeader'}, s} end + local htsnum = function(s) return { + {'ShaDaEntryHeader', 'ShaDaEntryTimestamp', 'ShaDaEntryTimestampNumber'}, + s + } end + local synhtssep = function(s) + return {{'ShaDaEntryHeader', 'ShaDaEntryTimestamp'}, s} + end + local synepoch = { + year = htsnum(os.date('%Y', 0)), + month = htsnum(os.date('%m', 0)), + day = htsnum(os.date('%d', 0)), + hour = htsnum(os.date('%H', 0)), + minute = htsnum(os.date('%M', 0)), + second = htsnum(os.date('%S', 0)), + } + local msh = function(s) return {{'ShaDaEntryMapShort', + 'ShaDaEntryMapHeader'}, s} end + local mlh = function(s) return {{'ShaDaEntryMapLong', + 'ShaDaEntryMapHeader'}, s} end + local ah = function(s) return {{'ShaDaEntryArray', + 'ShaDaEntryArrayHeader'}, s} end + local mses = function(s) return {{'ShaDaEntryMapShort', + 'ShaDaEntryMapShortEntryStart'}, s} end + local mles = function(s) return {{'ShaDaEntryMapLong', + 'ShaDaEntryMapLongEntryStart'}, s} end + local act = funcs.GetSyntax() + local ms = function(syn) + return { + {'ShaDaEntryMap' .. syn, 'ShaDaEntryMap' .. syn .. 'EntryStart'}, ' + ' + } + end + local as = function() + return {{'ShaDaEntryArray', 'ShaDaEntryArrayEntryStart'}, ' - '} + end + local ad = function(s) return {{'ShaDaEntryArray', + 'ShaDaEntryArrayDescription'}, s} end + local mbas = function(syn) + return { + {'ShaDaEntryMap' .. syn, 'ShaDaEntryMapBinArrayStart'}, + ' | - ' + } + end + local msk = function(s) return {{'ShaDaEntryMapShort', + 'ShaDaEntryMapShortKey'}, s} end + local mlk = function(s) return {{'ShaDaEntryMapLong', + 'ShaDaEntryMapLongKey'}, s} end + local mld = function(s) return {{'ShaDaEntryMapLong', + 'ShaDaEntryMapLongDescription'}, s} end + local c = function(s) return {{'ShaDaComment'}, s} end + local exp = { + { + hname('Header'), h(' with timestamp '), + synepoch.year, synhtssep('-'), synepoch.month, synhtssep('-'), + synepoch.day, synhtssep('T'), synepoch.hour, synhtssep(':'), + synepoch.minute, synhtssep(':'), synepoch.second, h(':'), + }, + { + msh(' % Key Value'), + }, + { + ms('Short'), msk('t '), + {{'ShaDaEntryMapShort', 'ShaDaMsgpackBinaryString', + 'ShaDaMsgpackStringQuotes'}, '"'}, + {{'ShaDaEntryMapShort', 'ShaDaMsgpackBinaryString'}, 'test'}, + {{'ShaDaEntryMapShort', 'ShaDaMsgpackStringQuotes'}, '"'}, + }, + { + hname('Jump'), h(' with timestamp '), + synepoch.year, synhtssep('-'), synepoch.month, synhtssep('-'), + synepoch.day, synhtssep('T'), synepoch.hour, synhtssep(':'), + synepoch.minute, synhtssep(':'), synepoch.second, h(':'), + }, + { + mlh(' % Key________ Description Value'), + }, + { + ms('Long'), mlk('n '), mld('name '), + {{'ShaDaEntryMapLong', 'ShaDaMsgpackCharacter'}, '\'A\''}, + }, + { + ms('Long'), mlk('f '), mld('file name '), + {{'ShaDaEntryMapLong', 'ShaDaMsgpackArray', + 'ShaDaMsgpackArrayBraces'}, '['}, + {{'ShaDaEntryMapLong', 'ShaDaMsgpackArray', 'ShaDaMsgpackBinaryString', + 'ShaDaMsgpackStringQuotes'}, '"'}, + {{'ShaDaEntryMapLong', 'ShaDaMsgpackArray', 'ShaDaMsgpackBinaryString'}, + 'foo'}, + {{'ShaDaEntryMapLong', 'ShaDaMsgpackArray', 'ShaDaMsgpackStringQuotes'}, + '"'}, + {{'ShaDaEntryMapLong', 'ShaDaMsgpackArrayBraces'}, ']'}, + }, + { + ms('Long'), mlk('l '), mld('line number '), + {{'ShaDaEntryMapLong', 'ShaDaMsgpackInteger'}, '2'}, + }, + { + ms('Long'), mlk('c '), mld('column '), + {{'ShaDaEntryMapLong', 'ShaDaMsgpackInteger'}, '-200'}, + }, + { + hname('Register'), h(' with timestamp '), + synepoch.year, synhtssep('-'), synepoch.month, synhtssep('-'), + synepoch.day, synhtssep('T'), synepoch.hour, synhtssep(':'), + synepoch.minute, synhtssep(':'), synepoch.second, h(':'), + }, + { + mlh(' % Key Description Value'), + }, + { + ms('Long'), mlk('rc '), mld('contents '), + {{'ShaDaEntryMapLong', 'ShaDaMsgpackMultilineArray'}, '@'}, + }, + { + mbas('Long'), + {{'ShaDaEntryMapLong', 'ShaDaMsgpackMap', 'ShaDaMsgpackMapBraces'}, + '{'}, + {{'ShaDaEntryMapLong', 'ShaDaMsgpackMap', 'ShaDaMsgpackBinaryString', + 'ShaDaMsgpackStringQuotes'}, '"'}, + {{'ShaDaEntryMapLong', 'ShaDaMsgpackMap', 'ShaDaMsgpackBinaryString'}, + 'abcdefghijklmnopqrstuvwxyz'}, + {{'ShaDaEntryMapLong', 'ShaDaMsgpackMap', 'ShaDaMsgpackStringQuotes'}, + '"'}, + {{'ShaDaEntryMapLong', 'ShaDaMsgpackMap', 'ShaDaMsgpackColon'}, ':'}, + {{'ShaDaEntryMapLong', 'ShaDaMsgpackMap'}, ' '}, + {{'ShaDaEntryMapLong', 'ShaDaMsgpackMap', 'ShaDaMsgpackFloat'}, '1.0'}, + {{'ShaDaEntryMapLong', 'ShaDaMsgpackMapBraces'}, '}'}, + }, + { + ms('Long'), mlk('rt '), mld('type '), + {{'ShaDaEntryMapLong', 'ShaDaMsgpackShaDaKeyword'}, 'CHARACTERWISE'}, + }, + { + ms('Long'), mlk('rt '), mld('type '), + {{'ShaDaEntryMapLong', 'ShaDaMsgpackShaDaKeyword'}, 'LINEWISE'}, + }, + { + ms('Long'), mlk('rt '), mld('type '), + {{'ShaDaEntryMapLong', 'ShaDaMsgpackShaDaKeyword'}, 'BLOCKWISE'}, + }, + { + hname('Replacement string'), h(' with timestamp '), + synepoch.year, synhtssep('-'), synepoch.month, synhtssep('-'), + synepoch.day, synhtssep('T'), synepoch.hour, synhtssep(':'), + synepoch.minute, synhtssep(':'), synepoch.second, h(':'), + }, + { + ah(' @ Description__________ Value'), + }, + { + as(), ad(':s replacement string '), + {{'ShaDaEntryArray', 'ShaDaMsgpackShaDaKeyword'}, 'CMD'}, + }, + { + as(), ad(':s replacement string '), + {{'ShaDaEntryArray', 'ShaDaMsgpackShaDaKeyword'}, 'SEARCH'}, + }, + { + as(), ad(':s replacement string '), + {{'ShaDaEntryArray', 'ShaDaMsgpackShaDaKeyword'}, 'EXPR'}, + }, + { + as(), ad(':s replacement string '), + {{'ShaDaEntryArray', 'ShaDaMsgpackShaDaKeyword'}, 'INPUT'}, + }, + { + {{'ShaDaEntryArrayEntryStart'}, ' - '}, + {{'ShaDaEntryArrayDescription'}, ':s replacement string '}, + {{'ShaDaMsgpackShaDaKeyword'}, 'DEBUG'}, + }, + { + hname('Buffer list'), h(' with timestamp '), + synepoch.year, synhtssep('-'), synepoch.month, synhtssep('-'), + synepoch.day, synhtssep('T'), synepoch.hour, synhtssep(':'), + synepoch.minute, synhtssep(':'), synepoch.second, h(':'), + }, + { + c(' # Expected array of maps'), + }, + { + {{'ShaDaEntryRawMsgpack'}, ' = '}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackArrayBraces'}, '['}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackMapBraces'}, + '{'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackString'}, '='}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackBinaryString', + 'ShaDaMsgpackStringQuotes'}, '"'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackBinaryString'}, + 'a'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackStringQuotes'}, + '"'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackColon'}, ':'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap'}, ' '}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackExt'}, '+('}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackExt', + 'ShaDaMsgpackExtType'}, '10'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackExt'}, ')'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackBinaryString', + 'ShaDaMsgpackStringQuotes'}, '"'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackBinaryString'}, + 'ac'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackBinaryString', + 'ShaDaMsgpackBinaryStringEscape'}, + '\\0'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackBinaryString'}, + 'df'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackBinaryString', + 'ShaDaMsgpackBinaryStringEscape'}, + '\\n'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackBinaryString'}, + 'gi'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackBinaryString', + 'ShaDaMsgpackBinaryStringEscape'}, + '\\"'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackBinaryString'}, + 'tt\\.'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackStringQuotes'}, + '"'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackComma'}, ','}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap'}, ' '}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackKeyword'}, + 'TRUE'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackColon'}, ':'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap'}, ' '}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMap', 'ShaDaMsgpackKeyword'}, + 'FALSE'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackMapBraces'}, '}'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackComma'}, ','}, + {{'ShaDaMsgpackArray'}, ' '}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackArray', 'ShaDaMsgpackArrayBraces'}, + '['}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackArray', 'ShaDaMsgpackKeyword'}, + 'NIL'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackArray', 'ShaDaMsgpackComma'}, ','}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackArray'}, ' '}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackArray', 'ShaDaMsgpackExt'}, '+('}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackArray', 'ShaDaMsgpackExt', + 'ShaDaMsgpackExtType'}, '-10'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackArray', 'ShaDaMsgpackExt'}, ')'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackArray', 'ShaDaMsgpackBinaryString', + 'ShaDaMsgpackStringQuotes'}, '"'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackArray', 'ShaDaMsgpackStringQuotes'}, + '"'}, + {{'ShaDaMsgpackArray', 'ShaDaMsgpackArrayBraces'}, ']'}, + {{'ShaDaMsgpackArrayBraces'}, ']'}, + }, + { + hname('Buffer list'), h(' with timestamp '), + synepoch.year, synhtssep('-'), synepoch.month, synhtssep('-'), + synepoch.day, synhtssep('T'), synepoch.hour, synhtssep(':'), + synepoch.minute, synhtssep(':'), synepoch.second, h(':'), + }, + { + mlh(' % Key Description Value'), + }, + { + }, + { + mlh(' % Key Description Value'), + }, + { + hname('Header'), h(' with timestamp '), + synepoch.year, synhtssep('-'), synepoch.month, synhtssep('-'), + synepoch.day, synhtssep('T'), synepoch.hour, synhtssep(':'), + synepoch.minute, synhtssep(':'), synepoch.second, h(':'), + }, + { + mlh(' % Key Description________ Value'), + }, + { + {{'ShaDaEntryMapLongEntryStart'}, ' + '}, + {{'ShaDaEntryMapLongKey'}, 'se '}, + {{'ShaDaEntryMapLongDescription'}, 'place cursor at end '}, + {{'ShaDaMsgpackKeyword'}, 'TRUE'}, + }, + } + eq(exp, act) + end) +end) |