diff options
-rw-r--r-- | test/unit/eval/typval_spec.lua | 180 |
1 files changed, 154 insertions, 26 deletions
diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index f2cb593cf5..0967e38eef 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -6,49 +6,48 @@ local itp = helpers.gen_itp(it) local eq = helpers.eq local neq = helpers.neq local ffi = helpers.ffi -local NULL = helpers.NULL local cimport = helpers.cimport local alloc_log_new = helpers.alloc_log_new local a = eval_helpers.alloc_logging_helpers local list = eval_helpers.list -local lst2tbl = eval_helpers.lst2tbl local type_key = eval_helpers.type_key +local li_alloc = eval_helpers.li_alloc local dict_type = eval_helpers.dict_type +local list_type = eval_helpers.list_type local lua2typvalt = eval_helpers.lua2typvalt local lib = cimport('./src/nvim/eval/typval.h', './src/nvim/memory.h') -local function li_alloc(nogc) - local gcfunc = lib.tv_list_item_free - if nogc then gcfunc = nil end - local li = ffi.gc(lib.tv_list_item_alloc(), gcfunc) - li.li_next = nil - li.li_prev = nil - li.li_tv = {v_type=lib.VAR_UNKNOWN, v_lock=lib.VAR_UNLOCKED} - return li -end - -local function list_index(l, idx) - return tv_list_find(l, idx) -end - local function list_items(l) local lis = {} local li = l.lv_first for i = 1, l.lv_len do - lis[i] = li + lis[i] = ffi.gc(li, nil) li = li.li_next end return lis end -local function list_watch(li) - return ffi.new('listwatch_T', {lw_item=li}) +local function list_watch_alloc(li) + return ffi.cast('listwatch_T*', ffi.new('listwatch_T[1]', {{lw_item=li}})) end -local alloc_log -local restore_allocators +local function list_watch(l, li) + local lw = list_watch_alloc(li or l.lv_first) + lib.tv_list_watch_add(l, lw) + return lw +end + +local function get_alloc_rets(exp_log, res) + for i = 1,#exp_log do + if ({malloc=true, calloc=true})[exp_log[i].func] then + res[#res + 1] = exp_log[i].ret + end + end + res.freed = function(r, n) return {func='free', args={r[n]}} end + return exp_log +end local to_cstr_nofree = function(v) return lib.xstrdup(v) end @@ -122,7 +121,7 @@ describe('typval.c', function() li = li_alloc(true) li.li_tv.v_type = lib.VAR_LIST - l = list() + l = ffi.gc(list(), nil) l.lv_refcount = 2 li.li_tv.vval.v_list = l lib.tv_list_item_free(li) @@ -185,26 +184,155 @@ describe('typval.c', function() neq(nil, l) local lis = list_items(l) -- Three watchers: pointing to first, middle and last elements. - local lws = {list_watch(lis[1]), list_watch(lis[4]), list_watch(lis[7])} - lib.tv_list_watch_add(l, lws[1]) - lib.tv_list_watch_add(l, lws[2]) - lib.tv_list_watch_add(l, lws[3]) + local lws = { + list_watch(l, lis[1]), + list_watch(l, lis[4]), + list_watch(l, lis[7]), + } lib.tv_list_item_remove(l, lis[4]) + ffi.gc(lis[4], lib.tv_list_item_free) eq({lis[1], lis[5], lis[7]}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item}) lib.tv_list_item_remove(l, lis[2]) + ffi.gc(lis[2], lib.tv_list_item_free) eq({lis[1], lis[5], lis[7]}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item}) lib.tv_list_item_remove(l, lis[7]) + ffi.gc(lis[7], lib.tv_list_item_free) eq({lis[1], lis[5], nil}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item == nil and nil}) lib.tv_list_item_remove(l, lis[1]) + ffi.gc(lis[1], lib.tv_list_item_free) eq({lis[3], lis[5], nil}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item == nil and nil}) + alloc_log:clear() lib.tv_list_free(l) + alloc_log:check({ + a.freed(lis[3]), + a.freed(lis[5]), + a.freed(lis[6]), + a.freed(l), + }) end) end) end) + describe('watch', function() + describe('remove()', function() + itp('works', function() + local l = ffi.gc(list(1, 2, 3, 4, 5, 6, 7), nil) + eq(nil, l.lv_watch) + local lw = list_watch(l) + neq(nil, l.lv_watch) + alloc_log:clear() + lib.tv_list_watch_remove(l, lw) + eq(nil, l.lv_watch) + alloc_log:check({ + -- Does not free anything. + }) + local lws = { list_watch(l), list_watch(l), list_watch(l) } + alloc_log:clear() + lib.tv_list_watch_remove(l, lws[2]) + eq(lws[3], l.lv_watch) + eq(lws[1], l.lv_watch.lw_next) + lib.tv_list_watch_remove(l, lws[1]) + eq(lws[3], l.lv_watch) + eq(nil, l.lv_watch.lw_next) + lib.tv_list_watch_remove(l, lws[3]) + eq(nil, l.lv_watch) + alloc_log:check({ + -- Does not free anything. + }) + end) + itp('ignores not found watchers', function() + local l = list(1, 2, 3, 4, 5, 6, 7) + local lw = list_watch_alloc() + lib.tv_list_watch_remove(l, lw) + end) + end) + end) + -- add() and fix() were tested when testing tv_list_item_remove() + describe('alloc()/free()', function() + itp('recursively frees list with', function() + local l1 = ffi.gc(list(1, 'abc'), nil) + local l2 = ffi.gc(list({[type_key]=dict_type}), nil) + local l3 = ffi.gc(list({[type_key]=list_type}), nil) + local alloc_rets = {} + alloc_log:check(get_alloc_rets({ + a.list(l1), + a.li(l1.lv_first), + a.str(l1.lv_last.li_tv.vval.v_string, #('abc')), + a.li(l1.lv_last), + a.list(l2), + a.dict(l2.lv_first.li_tv.vval.v_dict), + a.li(l2.lv_first), + a.list(l3), + a.list(l3.lv_first.li_tv.vval.v_list), + a.li(l3.lv_first), + }, alloc_rets)) + lib.tv_list_free(l1) + alloc_log:check({ + alloc_rets:freed(2), + alloc_rets:freed(3), + alloc_rets:freed(4), + alloc_rets:freed(1), + }) + lib.tv_list_free(l2) + alloc_log:check({ + alloc_rets:freed(6), + alloc_rets:freed(7), + alloc_rets:freed(5), + }) + lib.tv_list_free(l3) + alloc_log:check({ + alloc_rets:freed(9), + alloc_rets:freed(10), + alloc_rets:freed(8), + }) + end) + itp('frees all containers inside a list', function() + local l1 = ffi.gc(list('abc', {[type_key]=dict_type}, {[type_key]=list_type}), nil) + local alloc_rets = {} + alloc_log:check(get_alloc_rets({ + a.list(l1), + a.str(l1.lv_first.li_tv.vval.v_string, #('abc')), + a.li(l1.lv_first), + a.dict(l1.lv_first.li_next.li_tv.vval.v_dict), + a.li(l1.lv_first.li_next), + a.list(l1.lv_last.li_tv.vval.v_list), + a.li(l1.lv_last), + }, alloc_rets)) + lib.tv_list_free(l1) + alloc_log:check({ + alloc_rets:freed(2), + alloc_rets:freed(3), + alloc_rets:freed(4), + alloc_rets:freed(5), + alloc_rets:freed(6), + alloc_rets:freed(7), + alloc_rets:freed(1), + }) + end) + end) + describe('unref()', function() + itp('recursively frees list when reference count goes to 0', function() + local l = ffi.gc(list({[type_key]=list_type}), nil) + local alloc_rets = {} + alloc_log:check(get_alloc_rets({ + a.list(l), + a.list(l.lv_first.li_tv.vval.v_list), + a.li(l.lv_first), + }, alloc_rets)) + l.lv_refcount = 2 + lib.tv_list_unref(l) + alloc_log:check({}) + lib.tv_list_unref(l) + alloc_log:check({ + alloc_rets:freed(2), + alloc_rets:freed(3), + alloc_rets:freed(1), + }) + end) + end) end) end) |