test(unit/eval/typval_spec): adjust for Float to String conversion

Adjust relevant Lua tests. Refactor testing logic for tv_get_string_*
functions into test_string_fn().

Note that vim_snprintf(), which is used for stringifying floats, always
calls xfree(tofree), even if tofree is NULL, so we need to expect that
in the alloc log.
This commit is contained in:
Sean Dewar
2021-09-11 21:25:29 +01:00
committed by zeertzjq
parent 7abfb1f86e
commit 41ee7bc7db

View File

@@ -1430,15 +1430,16 @@ describe('typval.c', function()
end end
describe('str()', function() describe('str()', function()
itp('returns correct string', function() itp('returns correct string', function()
local l = list(int(1), int(2), int(3), int(4), int(5)) local l = list(int(1), 2.5, int(3), int(4), int(5))
alloc_log:clear() alloc_log:clear()
eq('1', tv_list_find_str(l, -5)) eq('1', tv_list_find_str(l, -5))
eq('2.5', tv_list_find_str(l, 1))
eq('5', tv_list_find_str(l, 4)) eq('5', tv_list_find_str(l, 4))
eq('3', tv_list_find_str(l, 2)) eq('3', tv_list_find_str(l, 2))
eq('3', tv_list_find_str(l, -3)) eq('3', tv_list_find_str(l, -3))
alloc_log:check({}) alloc_log:check({a.freed(alloc_log.null)})
end) end)
itp('returns string when used with VAR_STRING items', function() itp('returns string when used with VAR_STRING items', function()
local l = list('1', '2', '3', '4', '5') local l = list('1', '2', '3', '4', '5')
@@ -1466,14 +1467,12 @@ describe('typval.c', function()
eq(nil, tv_list_find_str(l, 5, 'E684: List index out of range: 5')) eq(nil, tv_list_find_str(l, 5, 'E684: List index out of range: 5'))
end) end)
itp('fails with error message on invalid types', function() itp('fails with error message on invalid types', function()
local l = list(1, empty_list, {}) local l = list(empty_list, {})
eq('', tv_list_find_str(l, 0, 'E806: Using a Float as a String')) eq('', tv_list_find_str(l, 0, 'E730: Using a List as a String'))
eq('', tv_list_find_str(l, 1, 'E730: Using a List as a String')) eq('', tv_list_find_str(l, 1, 'E731: Using a Dictionary as a String'))
eq('', tv_list_find_str(l, 2, 'E731: Using a Dictionary as a String'))
eq('', tv_list_find_str(l, -1, 'E731: Using a Dictionary as a String')) eq('', tv_list_find_str(l, -1, 'E731: Using a Dictionary as a String'))
eq('', tv_list_find_str(l, -2, 'E730: Using a List as a String')) eq('', tv_list_find_str(l, -2, 'E730: Using a List as a String'))
eq('', tv_list_find_str(l, -3, 'E806: Using a Float as a String'))
end) end)
end) end)
end) end)
@@ -1766,18 +1765,24 @@ describe('typval.c', function()
neq(s42, s43) neq(s42, s43)
eq(s43, dis.te.di_tv.vval.v_string) eq(s43, dis.te.di_tv.vval.v_string)
alloc_log:check({}) alloc_log:check({})
eq('', ffi.string(check_emsg(function() return lib.tv_dict_get_string(d, 't', false) end, local s44 = check_emsg(function() return lib.tv_dict_get_string(d, 't', false) end,
'E806: Using a Float as a String'))) nil)
eq('44.0', ffi.string(s44))
alloc_log:check({a.freed(alloc_log.null)})
end) end)
itp('allocates a string copy when requested', function() itp('allocates a string copy when requested', function()
local function tv_dict_get_string_alloc(d, key, emsg) local function tv_dict_get_string_alloc(d, key, emsg, is_float)
alloc_log:clear() alloc_log:clear()
local ret = check_emsg(function() return lib.tv_dict_get_string(d, key, true) end, local ret = check_emsg(function() return lib.tv_dict_get_string(d, key, true) end,
emsg) emsg)
local s_ret = (ret ~= nil) and ffi.string(ret) or nil local s_ret = (ret ~= nil) and ffi.string(ret) or nil
if not emsg then if not emsg then
if s_ret then if s_ret then
if is_float then
alloc_log:check({a.freed(alloc_log.null), a.str(ret, s_ret)})
else
alloc_log:check({a.str(ret, s_ret)}) alloc_log:check({a.str(ret, s_ret)})
end
else else
alloc_log:check({}) alloc_log:check({})
end end
@@ -1793,19 +1798,23 @@ describe('typval.c', function()
eq('42', tv_dict_get_string_alloc(d, 'tes')) eq('42', tv_dict_get_string_alloc(d, 'tes'))
eq('45', tv_dict_get_string_alloc(d, 'xx')) eq('45', tv_dict_get_string_alloc(d, 'xx'))
eq('43', tv_dict_get_string_alloc(d, 'te')) eq('43', tv_dict_get_string_alloc(d, 'te'))
eq('', tv_dict_get_string_alloc(d, 't', 'E806: Using a Float as a String')) eq('44.0', tv_dict_get_string_alloc(d, 't', nil, true))
end) end)
end) end)
describe('get_string_buf()', function() describe('get_string_buf()', function()
local function tv_dict_get_string_buf(d, key, buf, emsg) local function tv_dict_get_string_buf(d, key, is_float, buf, emsg)
buf = buf or ffi.gc(lib.xmalloc(lib.NUMBUFLEN), lib.xfree) buf = buf or ffi.gc(lib.xmalloc(lib.NUMBUFLEN), lib.xfree)
alloc_log:clear() alloc_log:clear()
local ret = check_emsg(function() return lib.tv_dict_get_string_buf(d, key, buf) end, local ret = check_emsg(function() return lib.tv_dict_get_string_buf(d, key, buf) end,
emsg) emsg)
local s_ret = (ret ~= nil) and ffi.string(ret) or nil local s_ret = (ret ~= nil) and ffi.string(ret) or nil
if not emsg then if not emsg then
if is_float then
alloc_log:check({a.freed(alloc_log.null)})
else
alloc_log:check({}) alloc_log:check({})
end end
end
return s_ret, ret, buf return s_ret, ret, buf
end end
itp('works with NULL dict', function() itp('works with NULL dict', function()
@@ -1828,16 +1837,16 @@ describe('typval.c', function()
s, r, b = tv_dict_get_string_buf(d, 'test') s, r, b = tv_dict_get_string_buf(d, 'test')
neq(r, b) neq(r, b)
eq('tset', s) eq('tset', s)
s, r, b = tv_dict_get_string_buf(d, 't', nil, 'E806: Using a Float as a String') s, r, b = tv_dict_get_string_buf(d, 't', true)
neq(r, b) eq(r, b)
eq('', s) eq('1.0', s)
s, r, b = tv_dict_get_string_buf(d, 'te') s, r, b = tv_dict_get_string_buf(d, 'te')
eq(r, b) eq(r, b)
eq('2', s) eq('2', s)
end) end)
end) end)
describe('get_string_buf_chk()', function() describe('get_string_buf_chk()', function()
local function tv_dict_get_string_buf_chk(d, key, len, buf, def, emsg) local function tv_dict_get_string_buf_chk(d, key, is_float, len, buf, def, emsg)
buf = buf or ffi.gc(lib.xmalloc(lib.NUMBUFLEN), lib.xfree) buf = buf or ffi.gc(lib.xmalloc(lib.NUMBUFLEN), lib.xfree)
def = def or ffi.gc(lib.xstrdup('DEFAULT'), lib.xfree) def = def or ffi.gc(lib.xstrdup('DEFAULT'), lib.xfree)
len = len or #key len = len or #key
@@ -1846,8 +1855,12 @@ describe('typval.c', function()
emsg) emsg)
local s_ret = (ret ~= nil) and ffi.string(ret) or nil local s_ret = (ret ~= nil) and ffi.string(ret) or nil
if not emsg then if not emsg then
if is_float then
alloc_log:check({a.freed(alloc_log.null)})
else
alloc_log:check({}) alloc_log:check({})
end end
end
return s_ret, ret, buf, def return s_ret, ret, buf, def
end end
itp('works with NULL dict', function() itp('works with NULL dict', function()
@@ -1871,10 +1884,10 @@ describe('typval.c', function()
neq(r, b) neq(r, b)
neq(r, def) neq(r, def)
eq('tset', s) eq('tset', s)
s, r, b, def = tv_dict_get_string_buf_chk(d, 'test', 1, nil, nil, 'E806: Using a Float as a String') s, r, b, def = tv_dict_get_string_buf_chk(d, 'test', true, 1)
neq(r, b) eq(r, b)
neq(r, def) neq(r, def)
eq(nil, s) eq('1.0', s)
s, r, b, def = tv_dict_get_string_buf_chk(d, 'te') s, r, b, def = tv_dict_get_string_buf_chk(d, 'te')
eq(r, b) eq(r, b)
neq(r, def) neq(r, def)
@@ -2832,7 +2845,7 @@ describe('typval.c', function()
alloc_log:clear() alloc_log:clear()
for _, v in ipairs({ for _, v in ipairs({
{lib.VAR_NUMBER, nil}, {lib.VAR_NUMBER, nil},
{lib.VAR_FLOAT, 'E806: Using a Float as a String'}, {lib.VAR_FLOAT, nil},
{lib.VAR_PARTIAL, 'E729: Using a Funcref as a String'}, {lib.VAR_PARTIAL, 'E729: Using a Funcref as a String'},
{lib.VAR_FUNC, 'E729: Using a Funcref as a String'}, {lib.VAR_FUNC, 'E729: Using a Funcref as a String'},
{lib.VAR_LIST, 'E730: Using a List as a String'}, {lib.VAR_LIST, 'E730: Using a List as a String'},
@@ -2979,24 +2992,9 @@ describe('typval.c', function()
end end
end) end)
end) end)
describe('string()', function()
itp('works', function() local function test_string_fn(input, fn)
local buf = lib.tv_get_string(lua2typvalt(int(1))) for _, v in ipairs(input) do
local buf_chk = lib.tv_get_string_chk(lua2typvalt(int(1)))
neq(buf, buf_chk)
for _, v in ipairs({
{lib.VAR_NUMBER, {v_number=42}, nil, '42'},
{lib.VAR_STRING, {v_string=to_cstr('100500')}, nil, '100500'},
{lib.VAR_FLOAT, {v_float=42.53}, 'E806: Using a Float as a String', ''},
{lib.VAR_PARTIAL, {v_partial=NULL}, 'E729: Using a Funcref as a String', ''},
{lib.VAR_FUNC, {v_string=NULL}, 'E729: Using a Funcref as a String', ''},
{lib.VAR_LIST, {v_list=NULL}, 'E730: Using a List as a String', ''},
{lib.VAR_DICT, {v_dict=NULL}, 'E731: Using a Dictionary as a String', ''},
{lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, nil, 'v:null'},
{lib.VAR_BOOL, {v_bool=lib.kBoolVarTrue}, nil, 'v:true'},
{lib.VAR_BOOL, {v_bool=lib.kBoolVarFalse}, nil, 'v:false'},
{lib.VAR_UNKNOWN, nil, 'E908: Using an invalid value as a String', ''},
}) do
-- Using to_cstr in place of Neovim allocated string, cannot -- Using to_cstr in place of Neovim allocated string, cannot
-- tv_clear() that. -- tv_clear() that.
local tv = ffi.gc(typvalt(v[1], v[2]), nil) local tv = ffi.gc(typvalt(v[1], v[2]), nil)
@@ -3004,9 +3002,9 @@ describe('typval.c', function()
local emsg = v[3] local emsg = v[3]
local ret = v[4] local ret = v[4]
eq(ret, check_emsg(function() eq(ret, check_emsg(function()
local res = lib.tv_get_string(tv) local res, buf = fn(tv)
if tv.v_type == lib.VAR_NUMBER or tv.v_type == lib.VAR_SPECIAL if tv.v_type == lib.VAR_NUMBER or tv.v_type == lib.VAR_FLOAT
or tv.v_type == lib.VAR_BOOL then or tv.v_type == lib.VAR_SPECIAL or tv.v_type == lib.VAR_BOOL then
eq(buf, res) eq(buf, res)
else else
neq(buf, res) neq(buf, res)
@@ -3019,61 +3017,22 @@ describe('typval.c', function()
end, emsg)) end, emsg))
if emsg then if emsg then
alloc_log:clear() alloc_log:clear()
elseif tv.v_type == lib.VAR_FLOAT then
alloc_log:check({a.freed(alloc_log.null)})
else else
alloc_log:check({}) alloc_log:check({})
end end
end end
end) end
end) describe('string()', function()
describe('string_chk()', function()
itp('works', function() itp('works', function()
local buf = lib.tv_get_string_chk(lua2typvalt(int(1))) local buf = lib.tv_get_string(lua2typvalt(int(1)))
for _, v in ipairs({ local buf_chk = lib.tv_get_string_chk(lua2typvalt(int(1)))
neq(buf, buf_chk)
test_string_fn({
{lib.VAR_NUMBER, {v_number=42}, nil, '42'}, {lib.VAR_NUMBER, {v_number=42}, nil, '42'},
{lib.VAR_STRING, {v_string=to_cstr('100500')}, nil, '100500'}, {lib.VAR_STRING, {v_string=to_cstr('100500')}, nil, '100500'},
{lib.VAR_FLOAT, {v_float=42.53}, 'E806: Using a Float as a String', nil}, {lib.VAR_FLOAT, {v_float=42.53}, nil, '42.53'},
{lib.VAR_PARTIAL, {v_partial=NULL}, 'E729: Using a Funcref as a String', nil},
{lib.VAR_FUNC, {v_string=NULL}, 'E729: Using a Funcref as a String', nil},
{lib.VAR_LIST, {v_list=NULL}, 'E730: Using a List as a String', nil},
{lib.VAR_DICT, {v_dict=NULL}, 'E731: Using a Dictionary as a String', nil},
{lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, nil, 'v:null'},
{lib.VAR_BOOL, {v_bool=lib.kBoolVarTrue}, nil, 'v:true'},
{lib.VAR_BOOL, {v_bool=lib.kBoolVarFalse}, nil, 'v:false'},
{lib.VAR_UNKNOWN, nil, 'E908: Using an invalid value as a String', nil},
}) do
-- Using to_cstr, cannot free with tv_clear
local tv = ffi.gc(typvalt(v[1], v[2]), nil)
alloc_log:check({})
local emsg = v[3]
local ret = v[4]
eq(ret, check_emsg(function()
local res = lib.tv_get_string_chk(tv)
if tv.v_type == lib.VAR_NUMBER or tv.v_type == lib.VAR_SPECIAL
or tv.v_type == lib.VAR_BOOL then
eq(buf, res)
else
neq(buf, res)
end
if res ~= nil then
return ffi.string(res)
else
return nil
end
end, emsg))
if emsg then
alloc_log:clear()
else
alloc_log:check({})
end
end
end)
end)
describe('string_buf()', function()
itp('works', function()
for _, v in ipairs({
{lib.VAR_NUMBER, {v_number=42}, nil, '42'},
{lib.VAR_STRING, {v_string=to_cstr('100500')}, nil, '100500'},
{lib.VAR_FLOAT, {v_float=42.53}, 'E806: Using a Float as a String', ''},
{lib.VAR_PARTIAL, {v_partial=NULL}, 'E729: Using a Funcref as a String', ''}, {lib.VAR_PARTIAL, {v_partial=NULL}, 'E729: Using a Funcref as a String', ''},
{lib.VAR_FUNC, {v_string=NULL}, 'E729: Using a Funcref as a String', ''}, {lib.VAR_FUNC, {v_string=NULL}, 'E729: Using a Funcref as a String', ''},
{lib.VAR_LIST, {v_list=NULL}, 'E730: Using a List as a String', ''}, {lib.VAR_LIST, {v_list=NULL}, 'E730: Using a List as a String', ''},
@@ -3082,41 +3041,18 @@ describe('typval.c', function()
{lib.VAR_BOOL, {v_bool=lib.kBoolVarTrue}, nil, 'v:true'}, {lib.VAR_BOOL, {v_bool=lib.kBoolVarTrue}, nil, 'v:true'},
{lib.VAR_BOOL, {v_bool=lib.kBoolVarFalse}, nil, 'v:false'}, {lib.VAR_BOOL, {v_bool=lib.kBoolVarFalse}, nil, 'v:false'},
{lib.VAR_UNKNOWN, nil, 'E908: Using an invalid value as a String', ''}, {lib.VAR_UNKNOWN, nil, 'E908: Using an invalid value as a String', ''},
}) do }, function(tv)
-- Using to_cstr, cannot free with tv_clear return lib.tv_get_string(tv), buf
local tv = ffi.gc(typvalt(v[1], v[2]), nil)
alloc_log:check({})
local emsg = v[3]
local ret = v[4]
eq(ret, check_emsg(function()
local buf = ffi.new('char[?]', lib.NUMBUFLEN, {0})
local res = lib.tv_get_string_buf(tv, buf)
if tv.v_type == lib.VAR_NUMBER or tv.v_type == lib.VAR_SPECIAL
or tv.v_type == lib.VAR_BOOL then
eq(buf, res)
else
neq(buf, res)
end
if res ~= nil then
return ffi.string(res)
else
return nil
end
end, emsg))
if emsg then
alloc_log:clear()
else
alloc_log:check({})
end
end
end) end)
end) end)
describe('string_buf_chk()', function() end)
describe('string_chk()', function()
itp('works', function() itp('works', function()
for _, v in ipairs({ local buf = lib.tv_get_string_chk(lua2typvalt(int(1)))
test_string_fn({
{lib.VAR_NUMBER, {v_number=42}, nil, '42'}, {lib.VAR_NUMBER, {v_number=42}, nil, '42'},
{lib.VAR_STRING, {v_string=to_cstr('100500')}, nil, '100500'}, {lib.VAR_STRING, {v_string=to_cstr('100500')}, nil, '100500'},
{lib.VAR_FLOAT, {v_float=42.53}, 'E806: Using a Float as a String', nil}, {lib.VAR_FLOAT, {v_float=42.53}, nil, '42.53'},
{lib.VAR_PARTIAL, {v_partial=NULL}, 'E729: Using a Funcref as a String', nil}, {lib.VAR_PARTIAL, {v_partial=NULL}, 'E729: Using a Funcref as a String', nil},
{lib.VAR_FUNC, {v_string=NULL}, 'E729: Using a Funcref as a String', nil}, {lib.VAR_FUNC, {v_string=NULL}, 'E729: Using a Funcref as a String', nil},
{lib.VAR_LIST, {v_list=NULL}, 'E730: Using a List as a String', nil}, {lib.VAR_LIST, {v_list=NULL}, 'E730: Using a List as a String', nil},
@@ -3125,33 +3061,49 @@ describe('typval.c', function()
{lib.VAR_BOOL, {v_bool=lib.kBoolVarTrue}, nil, 'v:true'}, {lib.VAR_BOOL, {v_bool=lib.kBoolVarTrue}, nil, 'v:true'},
{lib.VAR_BOOL, {v_bool=lib.kBoolVarFalse}, nil, 'v:false'}, {lib.VAR_BOOL, {v_bool=lib.kBoolVarFalse}, nil, 'v:false'},
{lib.VAR_UNKNOWN, nil, 'E908: Using an invalid value as a String', nil}, {lib.VAR_UNKNOWN, nil, 'E908: Using an invalid value as a String', nil},
}) do }, function(tv)
-- Using to_cstr, cannot free with tv_clear return lib.tv_get_string_chk(tv), buf
local tv = ffi.gc(typvalt(v[1], v[2]), nil) end)
alloc_log:check({}) end)
local emsg = v[3] end)
local ret = v[4] describe('string_buf()', function()
eq(ret, check_emsg(function() itp('works', function()
test_string_fn({
{lib.VAR_NUMBER, {v_number=42}, nil, '42'},
{lib.VAR_STRING, {v_string=to_cstr('100500')}, nil, '100500'},
{lib.VAR_FLOAT, {v_float=42.53}, nil, '42.53'},
{lib.VAR_PARTIAL, {v_partial=NULL}, 'E729: Using a Funcref as a String', ''},
{lib.VAR_FUNC, {v_string=NULL}, 'E729: Using a Funcref as a String', ''},
{lib.VAR_LIST, {v_list=NULL}, 'E730: Using a List as a String', ''},
{lib.VAR_DICT, {v_dict=NULL}, 'E731: Using a Dictionary as a String', ''},
{lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, nil, 'v:null'},
{lib.VAR_BOOL, {v_bool=lib.kBoolVarTrue}, nil, 'v:true'},
{lib.VAR_BOOL, {v_bool=lib.kBoolVarFalse}, nil, 'v:false'},
{lib.VAR_UNKNOWN, nil, 'E908: Using an invalid value as a String', ''},
}, function(tv)
local buf = ffi.new('char[?]', lib.NUMBUFLEN, {0}) local buf = ffi.new('char[?]', lib.NUMBUFLEN, {0})
local res = lib.tv_get_string_buf_chk(tv, buf) return lib.tv_get_string_buf(tv, buf), buf
if tv.v_type == lib.VAR_NUMBER or tv.v_type == lib.VAR_SPECIAL end)
or tv.v_type == lib.VAR_BOOL then end)
eq(buf, res) end)
else describe('string_buf_chk()', function()
neq(buf, res) itp('works', function()
end test_string_fn({
if res ~= nil then {lib.VAR_NUMBER, {v_number=42}, nil, '42'},
return ffi.string(res) {lib.VAR_STRING, {v_string=to_cstr('100500')}, nil, '100500'},
else {lib.VAR_FLOAT, {v_float=42.53}, nil, '42.53'},
return nil {lib.VAR_PARTIAL, {v_partial=NULL}, 'E729: Using a Funcref as a String', nil},
end {lib.VAR_FUNC, {v_string=NULL}, 'E729: Using a Funcref as a String', nil},
end, emsg)) {lib.VAR_LIST, {v_list=NULL}, 'E730: Using a List as a String', nil},
if emsg then {lib.VAR_DICT, {v_dict=NULL}, 'E731: Using a Dictionary as a String', nil},
alloc_log:clear() {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, nil, 'v:null'},
else {lib.VAR_BOOL, {v_bool=lib.kBoolVarTrue}, nil, 'v:true'},
alloc_log:check({}) {lib.VAR_BOOL, {v_bool=lib.kBoolVarFalse}, nil, 'v:false'},
end {lib.VAR_UNKNOWN, nil, 'E908: Using an invalid value as a String', nil},
end }, function(tv)
local buf = ffi.new('char[?]', lib.NUMBUFLEN, {0})
return lib.tv_get_string_buf_chk(tv, buf), buf
end)
end) end)
end) end)
end) end)