diff options
author | ZyX <kp-pav@yandex.ru> | 2017-01-03 22:51:29 +0300 |
---|---|---|
committer | ZyX <kp-pav@yandex.ru> | 2017-01-03 22:51:29 +0300 |
commit | 136b382e64390bf3129a7cd127b7704c983ec300 (patch) | |
tree | e2d352e1236e6ecb82f6c70478ecd74056f1bc21 | |
parent | efc624c2fe029a4ab494672c43366e8c57c19108 (diff) | |
download | rneovim-136b382e64390bf3129a7cd127b7704c983ec300.tar.gz rneovim-136b382e64390bf3129a7cd127b7704c983ec300.tar.bz2 rneovim-136b382e64390bf3129a7cd127b7704c983ec300.zip |
tests: Add tests for partials dumping
Also fixed dumping of partials by encode_vim_to_object and added code which is
able to work with partials and dictionaries to test/unit/eval/helpers.lua
(mostly copied from #5119, except for partials handling).
-rw-r--r-- | src/nvim/api/private/helpers.c | 7 | ||||
-rw-r--r-- | test/functional/eval/json_functions_spec.lua | 6 | ||||
-rw-r--r-- | test/functional/eval/msgpack_functions_spec.lua | 19 | ||||
-rw-r--r-- | test/functional/eval/string_spec.lua | 60 | ||||
-rw-r--r-- | test/unit/api/private_helpers_spec.lua | 18 | ||||
-rw-r--r-- | test/unit/eval/helpers.lua | 225 | ||||
-rw-r--r-- | test/unit/helpers.lua | 2 |
7 files changed, 289 insertions, 48 deletions
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 5bd76bc238..de566e37ad 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -354,10 +354,13 @@ void set_option_to(void *to, int type, String name, Object value, Error *err) #define TYPVAL_ENCODE_CONV_STR_STRING TYPVAL_ENCODE_CONV_STRING #define TYPVAL_ENCODE_CONV_EXT_STRING(tv, str, len, type) \ - TYPVAL_ENCODE_CONV_NIL() + TYPVAL_ENCODE_CONV_NIL(tv) #define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \ - TYPVAL_ENCODE_CONV_NIL() + do { \ + TYPVAL_ENCODE_CONV_NIL(tv); \ + goto typval_encode_stop_converting_one_item; \ + } while (0) #define TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, len) #define TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, len) diff --git a/test/functional/eval/json_functions_spec.lua b/test/functional/eval/json_functions_spec.lua index 4a6758019b..fc0a19bdfa 100644 --- a/test/functional/eval/json_functions_spec.lua +++ b/test/functional/eval/json_functions_spec.lua @@ -672,6 +672,12 @@ describe('json_encode() function', function() exc_exec('call json_encode(function("tr"))')) end) + it('fails to dump a partial', function() + execute('function T() dict\nendfunction') + eq('Vim(call):E474: Error while dumping encode_tv2json() argument, itself: attempt to dump function reference', + exc_exec('call json_encode(function("T", [1, 2], {}))')) + end) + it('fails to dump a function reference in a list', function() eq('Vim(call):E474: Error while dumping encode_tv2json() argument, index 0: attempt to dump function reference', exc_exec('call json_encode([function("tr")])')) diff --git a/test/functional/eval/msgpack_functions_spec.lua b/test/functional/eval/msgpack_functions_spec.lua index 3fb54c2ee7..44c01d2226 100644 --- a/test/functional/eval/msgpack_functions_spec.lua +++ b/test/functional/eval/msgpack_functions_spec.lua @@ -493,6 +493,12 @@ describe('msgpackparse() function', function() exc_exec('call msgpackparse(function("tr"))')) end) + it('fails to parse a partial', function() + execute('function T() dict\nendfunction') + eq('Vim(call):E686: Argument of msgpackparse() must be a List', + exc_exec('call msgpackparse(function("T", [1, 2], {}))')) + end) + it('fails to parse a float', function() eq('Vim(call):E686: Argument of msgpackparse() must be a List', exc_exec('call msgpackparse(0.0)')) @@ -570,6 +576,13 @@ describe('msgpackdump() function', function() exc_exec('call msgpackdump([Todump])')) end) + it('fails to dump a partial', function() + execute('function T() dict\nendfunction') + execute('let Todump = function("T", [1, 2], {})') + eq('Vim(call):E5004: Error while dumping msgpackdump() argument, index 0, itself: attempt to dump function reference', + exc_exec('call msgpackdump([Todump])')) + end) + it('fails to dump a function reference in a list', function() execute('let todump = [function("tr")]') eq('Vim(call):E5004: Error while dumping msgpackdump() argument, index 0, index 0: attempt to dump function reference', @@ -675,6 +688,12 @@ describe('msgpackdump() function', function() exc_exec('call msgpackdump(function("tr"))')) end) + it('fails to dump a partial', function() + execute('function T() dict\nendfunction') + eq('Vim(call):E686: Argument of msgpackdump() must be a List', + exc_exec('call msgpackdump(function("T", [1, 2], {}))')) + end) + it('fails to dump a float', function() eq('Vim(call):E686: Argument of msgpackdump() must be a List', exc_exec('call msgpackdump(0.0)')) diff --git a/test/functional/eval/string_spec.lua b/test/functional/eval/string_spec.lua index 1f963c674e..35caef702b 100644 --- a/test/functional/eval/string_spec.lua +++ b/test/functional/eval/string_spec.lua @@ -10,6 +10,7 @@ local funcs = helpers.funcs local write_file = helpers.write_file local NIL = helpers.NIL local source = helpers.source +local dedent = helpers.dedent describe('string() function', function() before_each(clear) @@ -111,10 +112,10 @@ describe('string() function', function() function Test1() endfunction - function s:Test2() + function s:Test2() dict endfunction - function g:Test3() + function g:Test3() dict endfunction let g:Test2_f = function('s:Test2') @@ -150,6 +151,61 @@ describe('string() function', function() eq("\nE724: unable to correctly dump variable with self-referencing container\nfunction('TestDict', {'tdr': function('TestDict', {E724@1})})", redir_exec('echo string(d.tdr)')) end) + + it('dumps automatically created partials', function() + eq('function(\'<SNR>1_Test2\', {\'f\': function(\'<SNR>1_Test2\')})', + eval('string({"f": Test2_f}.f)')) + eq('function(\'<SNR>1_Test2\', [1], {\'f\': function(\'<SNR>1_Test2\', [1])})', + eval('string({"f": function(Test2_f, [1])}.f)')) + end) + + it('dumps manually created partials', function() + eq('function(\'Test3\', [1, 2], {})', + eval('string(function("Test3", [1, 2], {}))')) + eq('function(\'Test3\', {})', + eval('string(function("Test3", {}))')) + eq('function(\'Test3\', [1, 2])', + eval('string(function("Test3", [1, 2]))')) + end) + + it('does not crash or halt when dumping partials with reference cycles in self', + function() + meths.set_var('d', {v=true}) + eq(dedent([[ + + E724: unable to correctly dump variable with self-referencing container + {'p': function('<SNR>1_Test2', {E724@0}), 'f': function('<SNR>1_Test2'), 'v': v:true}]]), + redir_exec('echo string(extend(extend(g:d, {"f": g:Test2_f}), {"p": g:d.f}))')) + end) + + it('does not crash or halt when dumping partials with reference cycles in arguments', + function() + meths.set_var('l', {}) + eval('add(l, l)') + -- Regression: the below line used to crash (add returns original list and + -- there was error in dumping partials). Tested explicitly in + -- test/unit/api/private_helpers_spec.lua. + eval('add(l, function("Test1", l))') + eq(dedent([=[ + + E724: unable to correctly dump variable with self-referencing container + function('Test1', [[{E724@2}, function('Test1', [{E724@2}])], function('Test1', [[{E724@4}, function('Test1', [{E724@4}])]])])]=]), + redir_exec('echo string(function("Test1", l))')) + end) + + it('does not crash or halt when dumping partials with reference cycles in self and arguments', + function() + meths.set_var('d', {v=true}) + meths.set_var('l', {}) + eval('add(l, l)') + eval('add(l, function("Test1", l))') + eval('add(l, function("Test1", d))') + eq(dedent([=[ + + E724: unable to correctly dump variable with self-referencing container + {'p': function('<SNR>1_Test2', [[{E724@3}, function('Test1', [{E724@3}]), function('Test1', {E724@0})], function('Test1', [[{E724@5}, function('Test1', [{E724@5}]), function('Test1', {E724@0})]]), function('Test1', {E724@0})], {E724@0}), 'f': function('<SNR>1_Test2'), 'v': v:true}]=]), + redir_exec('echo string(extend(extend(g:d, {"f": g:Test2_f}), {"p": function(g:d.f, l)}))')) + end) end) describe('used to represent lists', function() diff --git a/test/unit/api/private_helpers_spec.lua b/test/unit/api/private_helpers_spec.lua index 1d7c03787b..e108d46370 100644 --- a/test/unit/api/private_helpers_spec.lua +++ b/test/unit/api/private_helpers_spec.lua @@ -7,6 +7,7 @@ local NULL = helpers.NULL local eq = helpers.eq local lua2typvalt = eval_helpers.lua2typvalt +local typvalt2lua = eval_helpers.typvalt2lua local typvalt = eval_helpers.typvalt local nil_value = api_helpers.nil_value @@ -14,6 +15,7 @@ local list_type = api_helpers.list_type local int_type = api_helpers.int_type local type_key = api_helpers.type_key local obj2lua = api_helpers.obj2lua +local func_type = api_helpers.func_type local api = cimport('./src/nvim/api/private/helpers.h') @@ -85,4 +87,20 @@ describe('vim_to_object', function() eq(nil, tt.vval.v_dict) eq({}, obj2lua(api.vim_to_object(tt))) end) + + it('regression: partials in a list', function() + local llist = { + { + [type_key]=func_type, + value='printf', + args={'%s'}, + dict={v=1}, + }, + {}, + } + local ffi=require'ffi' + local list = lua2typvalt(llist) + eq(llist, typvalt2lua(list)) + eq({nil_value, {}}, obj2lua(api.vim_to_object(list))) + end) end) diff --git a/test/unit/eval/helpers.lua b/test/unit/eval/helpers.lua index 45fbf8da5c..0656257361 100644 --- a/test/unit/eval/helpers.lua +++ b/test/unit/eval/helpers.lua @@ -9,6 +9,7 @@ local eval = cimport('./src/nvim/eval.h', './src/nvim/eval_defs.h') local null_string = {[true]='NULL string'} local null_list = {[true]='NULL list'} +local null_dict = {[true]='NULL dict'} local type_key = {[true]='type key'} local list_type = {[true]='list type'} local dict_type = {[true]='dict type'} @@ -18,27 +19,28 @@ local flt_type = {[true]='flt type'} local nil_value = {[true]='nil'} +local lua2typvalt + +local function li_alloc(nogc) + local gcfunc = eval.listitem_free + if nogc then gcfunc = nil end + local li = ffi.gc(eval.listitem_alloc(), gcfunc) + li.li_next = nil + li.li_prev = nil + li.li_tv = {v_type=eval.VAR_UNKNOWN, v_lock=eval.VAR_UNLOCKED} + return li +end + local function list(...) local ret = ffi.gc(eval.list_alloc(), eval.list_unref) eq(0, ret.lv_refcount) ret.lv_refcount = 1 for i = 1, select('#', ...) do local val = select(i, ...) - local typ = type(val) - if typ == 'string' then - eval.list_append_string(ret, to_cstr(val)) - elseif typ == 'table' and val == null_string then - eval.list_append_string(ret, nil) - elseif typ == 'table' and val == null_list then - eval.list_append_list(ret, nil) - elseif typ == 'table' and val[type_key] == list_type then - local itemlist = ffi.gc(list(table.unpack(val)), nil) - eq(1, itemlist.lv_refcount) - itemlist.lv_refcount = 0 - eval.list_append_list(ret, itemlist) - else - assert(false, 'Not implemented yet') - end + local li_tv = ffi.gc(lua2typvalt(val), nil) + local li = li_alloc(true) + li.li_tv = li_tv + eval.tv_list_append(ret, li) end return ret end @@ -49,22 +51,27 @@ local special_tab = { [eval.kSpecialVarTrue] = true, } +local ptr2key = function(ptr) + return tostring(ptr) +end + local lst2tbl local dct2tbl +local typvalt2lua local typvalt2lua_tab typvalt2lua_tab = { - [tonumber(eval.VAR_SPECIAL)] = function(t) + [tonumber(eval.VAR_SPECIAL)] = function(t, processed) return special_tab[t.vval.v_special] end, - [tonumber(eval.VAR_NUMBER)] = function(t) + [tonumber(eval.VAR_NUMBER)] = function(t, processed) return {[type_key]=int_type, value=tonumber(t.vval.v_number)} end, - [tonumber(eval.VAR_FLOAT)] = function(t) + [tonumber(eval.VAR_FLOAT)] = function(t, processed) return tonumber(t.vval.v_float) end, - [tonumber(eval.VAR_STRING)] = function(t) + [tonumber(eval.VAR_STRING)] = function(t, processed) local str = t.vval.v_string if str == nil then return null_string @@ -72,32 +79,64 @@ typvalt2lua_tab = { return ffi.string(str) end end, - [tonumber(eval.VAR_LIST)] = function(t) - return lst2tbl(t.vval.v_list) + [tonumber(eval.VAR_LIST)] = function(t, processed) + return lst2tbl(t.vval.v_list, processed) end, - [tonumber(eval.VAR_DICT)] = function(t) - return dct2tbl(t.vval.v_dict) + [tonumber(eval.VAR_DICT)] = function(t, processed) + return dct2tbl(t.vval.v_dict, processed) end, - [tonumber(eval.VAR_FUNC)] = function(t) - return {[type_key]=func_type, value=typvalt2lua_tab[eval.VAR_STRING](t)} + [tonumber(eval.VAR_FUNC)] = function(t, processed) + return {[type_key]=func_type, value=typvalt2lua_tab[eval.VAR_STRING](t, processed or {})} + end, + [tonumber(eval.VAR_PARTIAL)] = function(t, processed) + local p_key = ptr2key(t) + if processed[p_key] then + return processed[p_key] + end + local pt = t.vval.v_partial + local value, auto, dict, argv = nil, nil, nil, nil + if pt ~= nil then + value = ffi.string(pt.pt_name) + auto = pt.pt_auto and true or nil + argv = {} + for i = 1, pt.pt_argc do + argv[i] = typvalt2lua(pt.pt_argv[i - 1], processed) + end + if pt.pt_dict ~= nil then + dict = dct2tbl(pt.pt_dict) + end + end + return { + [type_key]=func_type, + value=value, + auto=auto, + args=argv, + dict=dict, + } end, } -local typvalt2lua = function(t) - return ((typvalt2lua_tab[tonumber(t.v_type)] or function(t_inner) +typvalt2lua = function(t, processed) + return ((typvalt2lua_tab[tonumber(t.v_type)] or function(t_inner, processed) assert(false, 'Converting ' .. tonumber(t_inner.v_type) .. ' was not implemented yet') - end)(t)) + end)(t, processed or {})) end -lst2tbl = function(l) - local ret = {[type_key]=list_type} +lst2tbl = function(l, processed) if l == nil then - return ret + return null_list end + processed = processed or {} + local p_key = ptr2key(l) + if processed[p_key] then + return processed[p_key] + end + local ret = {[type_key]=list_type} + processed[p_key] = ret local li = l.lv_first -- (listitem_T *) NULL is equal to nil, but yet it is not false. while li ~= nil do - ret[#ret + 1] = typvalt2lua(li.li_tv) + ret[#ret + 1] = typvalt2lua(li.li_tv, processed) li = li.li_next end if ret[1] then @@ -106,16 +145,54 @@ lst2tbl = function(l) return ret end -dct2tbl = function(d) - local ret = {d=d} - assert(false, 'Converting dictionaries is not implemented yet') - return ret +local function dict_iter(d) + local init_s = { + todo=d.dv_hashtab.ht_used, + hi=d.dv_hashtab.ht_array, + } + local function f(s, _) + if s.todo == 0 then return nil end + while s.todo > 0 do + if s.hi.hi_key ~= nil and s.hi ~= eval.hash_removed then + local key = ffi.string(s.hi.hi_key) + local di = ffi.cast('dictitem_T*', + s.hi.hi_key - ffi.offsetof('dictitem_T', 'di_key')) + s.todo = s.todo - 1 + s.hi = s.hi + 1 + return key, di + end + s.hi = s.hi + 1 + end + end + return f, init_s, nil end -local lua2typvalt +local function first_di(d) + local f, init_s, v = dict_iter(d) + return select(2, f(init_s, v)) +end + +dct2tbl = function(d, processed) + if d == nil then + return null_dict + end + processed = processed or {} + local p_key = ptr2key(d) + if processed[p_key] then + return processed[p_key] + end + local ret = {} + processed[p_key] = ret + for k, di in dict_iter(d) do + ret[k] = typvalt2lua(di.di_tv, processed) + end + return ret +end local typvalt = function(typ, vval) - if type(typ) == 'string' then + if typ == nil then + typ = eval.VAR_UNKNOWN + elseif type(typ) == 'string' then typ = eval[typ] end return ffi.gc(ffi.new('typval_T', {v_type=typ, vval=vval}), eval.clear_tv) @@ -164,12 +241,68 @@ local lua2typvalt_type_tab = { end return ret end, + [func_type] = function(l, processed) + if processed[l] then + processed[l].pt_refcount = processed[l].pt_refcount + 1 + return typvalt(eval.VAR_PARTIAL, {v_partial=processed[l]}) + end + if l.args or l.dict then + local pt = ffi.gc(ffi.cast('partial_T*', eval.xmalloc(ffi.sizeof('partial_T'))), nil) + processed[l] = pt + local argv = nil + if l.args and #l.args > 0 then + argv = ffi.gc(ffi.cast('typval_T*', eval.xmalloc(ffi.sizeof('typval_T') * #l.args)), nil) + for i, arg in ipairs(l.args) do + local arg_tv = ffi.gc(lua2typvalt(arg, processed), nil) + eval.copy_tv(arg_tv, argv[i - 1]) + eval.clear_tv(arg_tv) + end + end + local dict = nil + if l.dict then + local dict_tv = ffi.gc(lua2typvalt(l.dict, processed), nil) + assert(dict_tv.v_type == eval.VAR_DICT) + dict = dict_tv.vval.v_dict + end + pt.pt_refcount = 1 + pt.pt_name = eval.xmemdupz(to_cstr(l.value), #l.value) + pt.pt_auto = not not l.auto + pt.pt_argc = l.args and #l.args or 0 + pt.pt_argv = argv + pt.pt_dict = dict + return typvalt(eval.VAR_PARTIAL, {v_partial=pt}) + else + return typvalt(eval.VAR_FUNC, { + v_string=eval.xmemdupz(to_cstr(l.value), #l.value) + }) + end + end, } +local special_vals = { + [null_string] = {eval.VAR_STRING, {v_string=ffi.cast('char_u*', nil)}}, + [null_list] = {eval.VAR_LIST, {v_list=ffi.cast('list_T*', nil)}}, + [null_dict] = {eval.VAR_DICT, {v_dict=ffi.cast('dict_T*', nil)}}, + [nil_value] = {eval.VAR_SPECIAL, {v_special=eval.kSpecialVarNull}}, + [true] = {eval.VAR_SPECIAL, {v_special=eval.kSpecialVarTrue}}, + [false] = {eval.VAR_SPECIAL, {v_special=eval.kSpecialVarFalse}}, +} + +for k, v in pairs(special_vals) do + local tmp = function(typ, vval) + special_vals[k] = function() + return typvalt(typ, vval) + end + end + tmp(v[1], v[2]) +end + lua2typvalt = function(l, processed) processed = processed or {} if l == nil or l == nil_value then - return typvalt(eval.VAR_SPECIAL, {v_special=eval.kSpecialVarNull}) + return special_vals[nil_value]() + elseif special_vals[l] then + return special_vals[l]() elseif type(l) == 'table' then if l[type_key] then return lua2typvalt_type_tab[l[type_key]](l, processed) @@ -182,18 +315,19 @@ lua2typvalt = function(l, processed) end elseif type(l) == 'number' then return typvalt(eval.VAR_FLOAT, {v_float=l}) - elseif type(l) == 'boolean' then - return typvalt(eval.VAR_SPECIAL, { - v_special=(l and eval.kSpecialVarTrue or eval.kSpecialVarFalse) - }) elseif type(l) == 'string' then return typvalt(eval.VAR_STRING, {v_string=eval.xmemdupz(to_cstr(l), #l)}) + elseif type(l) == 'cdata' then + local tv = typvalt(eval.VAR_UNKNOWN) + eval.tv_copy(l, tv) + return tv end end return { null_string=null_string, null_list=null_list, + null_dict=null_dict, list_type=list_type, dict_type=dict_type, func_type=func_type, @@ -212,4 +346,9 @@ return { typvalt2lua=typvalt2lua, typvalt=typvalt, + + li_alloc=li_alloc, + + dict_iter=dict_iter, + first_di=first_di, } diff --git a/test/unit/helpers.lua b/test/unit/helpers.lua index 3564f76442..9025c64570 100644 --- a/test/unit/helpers.lua +++ b/test/unit/helpers.lua @@ -136,7 +136,7 @@ end local cstr = ffi.typeof('char[?]') local function to_cstr(string) - return cstr((string.len(string)) + 1, string) + return cstr(#string + 1, string) end -- initialize some global variables, this is still necessary to unit test |