mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
unittests: Add tv_dict_add* unit tests
Also fixes incorrect location of `tv_dict_add` function and three bugs in other functions: 1. `tv_dict_add_list` may free list it does not own (vim/vim#1555). 2. `tv_dict_add_dict` may free dictionary it does not own (vim/vim#1555). 3. `tv_dict_add_dict` ignores `key_len` argument.
This commit is contained in:
parent
bc87d23c28
commit
270a3889af
@ -1009,18 +1009,6 @@ void tv_dict_item_free(dictitem_T *const item)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add item to dictionary
|
|
||||||
///
|
|
||||||
/// @param[out] d Dictionary to add to.
|
|
||||||
/// @param[in] item Item to add.
|
|
||||||
///
|
|
||||||
/// @return FAIL if key already exists.
|
|
||||||
int tv_dict_add(dict_T *const d, dictitem_T *const item)
|
|
||||||
FUNC_ATTR_NONNULL_ALL
|
|
||||||
{
|
|
||||||
return hash_add(&d->dv_hashtab, item->di_key);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Make a copy of a dictionary item
|
/// Make a copy of a dictionary item
|
||||||
///
|
///
|
||||||
/// @param[in] di Item to copy.
|
/// @param[in] di Item to copy.
|
||||||
@ -1278,6 +1266,18 @@ bool tv_dict_get_callback(dict_T *const d,
|
|||||||
|
|
||||||
//{{{2 dict_add*
|
//{{{2 dict_add*
|
||||||
|
|
||||||
|
/// Add item to dictionary
|
||||||
|
///
|
||||||
|
/// @param[out] d Dictionary to add to.
|
||||||
|
/// @param[in] item Item to add.
|
||||||
|
///
|
||||||
|
/// @return FAIL if key already exists.
|
||||||
|
int tv_dict_add(dict_T *const d, dictitem_T *const item)
|
||||||
|
FUNC_ATTR_NONNULL_ALL
|
||||||
|
{
|
||||||
|
return hash_add(&d->dv_hashtab, item->di_key);
|
||||||
|
}
|
||||||
|
|
||||||
/// Add a list entry to dictionary
|
/// Add a list entry to dictionary
|
||||||
///
|
///
|
||||||
/// @param[out] d Dictionary to add entry to.
|
/// @param[out] d Dictionary to add entry to.
|
||||||
@ -1295,11 +1295,11 @@ int tv_dict_add_list(dict_T *const d, const char *const key,
|
|||||||
item->di_tv.v_lock = VAR_UNLOCKED;
|
item->di_tv.v_lock = VAR_UNLOCKED;
|
||||||
item->di_tv.v_type = VAR_LIST;
|
item->di_tv.v_type = VAR_LIST;
|
||||||
item->di_tv.vval.v_list = list;
|
item->di_tv.vval.v_list = list;
|
||||||
|
list->lv_refcount++;
|
||||||
if (tv_dict_add(d, item) == FAIL) {
|
if (tv_dict_add(d, item) == FAIL) {
|
||||||
tv_dict_item_free(item);
|
tv_dict_item_free(item);
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
list->lv_refcount++;
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1315,16 +1315,16 @@ int tv_dict_add_dict(dict_T *const d, const char *const key,
|
|||||||
const size_t key_len, dict_T *const dict)
|
const size_t key_len, dict_T *const dict)
|
||||||
FUNC_ATTR_NONNULL_ALL
|
FUNC_ATTR_NONNULL_ALL
|
||||||
{
|
{
|
||||||
dictitem_T *const item = tv_dict_item_alloc(key);
|
dictitem_T *const item = tv_dict_item_alloc_len(key, key_len);
|
||||||
|
|
||||||
item->di_tv.v_lock = VAR_UNLOCKED;
|
item->di_tv.v_lock = VAR_UNLOCKED;
|
||||||
item->di_tv.v_type = VAR_DICT;
|
item->di_tv.v_type = VAR_DICT;
|
||||||
item->di_tv.vval.v_dict = dict;
|
item->di_tv.vval.v_dict = dict;
|
||||||
|
dict->dv_refcount++;
|
||||||
if (tv_dict_add(d, item) == FAIL) {
|
if (tv_dict_add(d, item) == FAIL) {
|
||||||
tv_dict_item_free(item);
|
tv_dict_item_free(item);
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
dict->dv_refcount++;
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1770,6 +1770,122 @@ describe('typval.c', function()
|
|||||||
{tv_dict_get_callback(d, 'testt', 5)})
|
{tv_dict_get_callback(d, 'testt', 5)})
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
describe('dict_add()', function()
|
||||||
|
itp('works', function()
|
||||||
|
local di = lib.tv_dict_item_alloc_len('t-est', 5)
|
||||||
|
alloc_log:check({a.di(di, 't-est')})
|
||||||
|
di.di_tv.v_type = lib.VAR_NUMBER
|
||||||
|
di.di_tv.vval.v_number = 42
|
||||||
|
local d = dict({test=10})
|
||||||
|
local dis = dict_items(d)
|
||||||
|
alloc_log:check({
|
||||||
|
a.dict(d),
|
||||||
|
a.di(dis.test, 'test')
|
||||||
|
})
|
||||||
|
eq({test=10}, dct2tbl(d))
|
||||||
|
alloc_log:clear()
|
||||||
|
eq(OK, lib.tv_dict_add(d, di))
|
||||||
|
alloc_log:check({})
|
||||||
|
eq({test=10, ['t-est']=int(42)}, dct2tbl(d))
|
||||||
|
eq(FAIL, check_emsg(function() return lib.tv_dict_add(d, di) end,
|
||||||
|
'E685: Internal error: hash_add()'))
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
describe('dict_add_list()', function()
|
||||||
|
itp('works', function()
|
||||||
|
local l = list(1, 2, 3)
|
||||||
|
alloc_log:clear()
|
||||||
|
eq(1, l.lv_refcount)
|
||||||
|
local d = dict({test=10})
|
||||||
|
alloc_log:clear()
|
||||||
|
eq({test=10}, dct2tbl(d))
|
||||||
|
eq(OK, lib.tv_dict_add_list(d, 'testt', 3, l))
|
||||||
|
local dis = dict_items(d)
|
||||||
|
alloc_log:check({a.di(dis.tes, 'tes')})
|
||||||
|
eq({test=10, tes={1, 2, 3}}, dct2tbl(d))
|
||||||
|
eq(2, l.lv_refcount)
|
||||||
|
eq(FAIL, check_emsg(function() return lib.tv_dict_add_list(d, 'testt', 3, l) end,
|
||||||
|
'E685: Internal error: hash_add()'))
|
||||||
|
eq(2, l.lv_refcount)
|
||||||
|
alloc_log:clear()
|
||||||
|
lib.emsg_skip = lib.emsg_skip + 1
|
||||||
|
eq(FAIL, check_emsg(function() return lib.tv_dict_add_list(d, 'testt', 3, l) end,
|
||||||
|
nil))
|
||||||
|
eq(2, l.lv_refcount)
|
||||||
|
lib.emsg_skip = lib.emsg_skip - 1
|
||||||
|
alloc_log:clear_tmp_allocs()
|
||||||
|
alloc_log:check({})
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
describe('dict_add_dict()', function()
|
||||||
|
itp('works', function()
|
||||||
|
local d2 = dict({foo=42})
|
||||||
|
alloc_log:clear()
|
||||||
|
eq(1, d2.dv_refcount)
|
||||||
|
local d = dict({test=10})
|
||||||
|
alloc_log:clear()
|
||||||
|
eq({test=10}, dct2tbl(d))
|
||||||
|
eq(OK, lib.tv_dict_add_dict(d, 'testt', 3, d2))
|
||||||
|
local dis = dict_items(d)
|
||||||
|
alloc_log:check({a.di(dis.tes, 'tes')})
|
||||||
|
eq({test=10, tes={foo=42}}, dct2tbl(d))
|
||||||
|
eq(2, d2.dv_refcount)
|
||||||
|
eq(FAIL, check_emsg(function() return lib.tv_dict_add_dict(d, 'testt', 3, d2) end,
|
||||||
|
'E685: Internal error: hash_add()'))
|
||||||
|
eq(2, d2.dv_refcount)
|
||||||
|
alloc_log:clear()
|
||||||
|
lib.emsg_skip = lib.emsg_skip + 1
|
||||||
|
eq(FAIL, check_emsg(function() return lib.tv_dict_add_dict(d, 'testt', 3, d2) end,
|
||||||
|
nil))
|
||||||
|
eq(2, d2.dv_refcount)
|
||||||
|
lib.emsg_skip = lib.emsg_skip - 1
|
||||||
|
alloc_log:clear_tmp_allocs()
|
||||||
|
alloc_log:check({})
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
describe('dict_add_nr()', function()
|
||||||
|
itp('works', function()
|
||||||
|
local d = dict({test=10})
|
||||||
|
alloc_log:clear()
|
||||||
|
eq({test=10}, dct2tbl(d))
|
||||||
|
eq(OK, lib.tv_dict_add_nr(d, 'testt', 3, 2))
|
||||||
|
local dis = dict_items(d)
|
||||||
|
alloc_log:check({a.di(dis.tes, 'tes')})
|
||||||
|
eq({test=10, tes=int(2)}, dct2tbl(d))
|
||||||
|
eq(FAIL, check_emsg(function() return lib.tv_dict_add_nr(d, 'testt', 3, 2) end,
|
||||||
|
'E685: Internal error: hash_add()'))
|
||||||
|
alloc_log:clear()
|
||||||
|
lib.emsg_skip = lib.emsg_skip + 1
|
||||||
|
eq(FAIL, check_emsg(function() return lib.tv_dict_add_nr(d, 'testt', 3, 2) end,
|
||||||
|
nil))
|
||||||
|
lib.emsg_skip = lib.emsg_skip - 1
|
||||||
|
alloc_log:clear_tmp_allocs()
|
||||||
|
alloc_log:check({})
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
describe('dict_add_str()', function()
|
||||||
|
itp('works', function()
|
||||||
|
local d = dict({test=10})
|
||||||
|
alloc_log:clear()
|
||||||
|
eq({test=10}, dct2tbl(d))
|
||||||
|
eq(OK, lib.tv_dict_add_str(d, 'testt', 3, 'TEST'))
|
||||||
|
local dis = dict_items(d)
|
||||||
|
alloc_log:check({
|
||||||
|
a.di(dis.tes, 'tes'),
|
||||||
|
a.str(dis.tes.di_tv.vval.v_string, 'TEST')
|
||||||
|
})
|
||||||
|
eq({test=10, tes='TEST'}, dct2tbl(d))
|
||||||
|
eq(FAIL, check_emsg(function() return lib.tv_dict_add_str(d, 'testt', 3, 'TEST') end,
|
||||||
|
'E685: Internal error: hash_add()'))
|
||||||
|
alloc_log:clear()
|
||||||
|
lib.emsg_skip = lib.emsg_skip + 1
|
||||||
|
eq(FAIL, check_emsg(function() return lib.tv_dict_add_str(d, 'testt', 3, 'TEST') end,
|
||||||
|
nil))
|
||||||
|
lib.emsg_skip = lib.emsg_skip - 1
|
||||||
|
alloc_log:clear_tmp_allocs()
|
||||||
|
alloc_log:check({})
|
||||||
|
end)
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
Loading…
Reference in New Issue
Block a user