mirror of
https://github.com/neovim/neovim.git
synced 2025-12-10 16:42:42 +00:00
Merge branch 'master' into s-dash-stdin
This commit is contained in:
@@ -2,12 +2,11 @@ local helpers = require('test.unit.helpers')(after_each)
|
||||
local itp = helpers.gen_itp(it)
|
||||
|
||||
local cimport = helpers.cimport
|
||||
local to_cstr = helpers.to_cstr
|
||||
local eq = helpers.eq
|
||||
local neq = helpers.neq
|
||||
local ffi = helpers.ffi
|
||||
|
||||
local decode = cimport('./src/nvim/eval/decode.h', './src/nvim/eval_defs.h',
|
||||
local decode = cimport('./src/nvim/eval/decode.h', './src/nvim/eval/typval.h',
|
||||
'./src/nvim/globals.h', './src/nvim/memory.h',
|
||||
'./src/nvim/message.h')
|
||||
|
||||
@@ -72,7 +71,7 @@ describe('json_decode_string()', function()
|
||||
end
|
||||
|
||||
itp('does not overflow in error messages', function()
|
||||
local saved_p_enc = decode.p_enc
|
||||
collectgarbage('restart')
|
||||
check_failure(']test', 1, 'E474: No container to close: ]')
|
||||
check_failure('[}test', 2, 'E474: Closing list with curly bracket: }')
|
||||
check_failure('{]test', 2,
|
||||
@@ -105,10 +104,6 @@ describe('json_decode_string()', function()
|
||||
check_failure('"\194"test', 3, 'E474: Only UTF-8 strings allowed: \194"')
|
||||
check_failure('"\252\144\128\128\128\128"test', 8, 'E474: Only UTF-8 code points up to U+10FFFF are allowed to appear unescaped: \252\144\128\128\128\128"')
|
||||
check_failure('"test', 1, 'E474: Expected string end: "')
|
||||
decode.p_enc = to_cstr('latin1')
|
||||
check_failure('"\\uABCD"test', 8,
|
||||
'E474: Failed to convert string "ꯍ" from UTF-8')
|
||||
decode.p_enc = saved_p_enc
|
||||
check_failure('-test', 1, 'E474: Missing number after minus sign: -')
|
||||
check_failure('-1.test', 3, 'E474: Missing number after decimal dot: -1.')
|
||||
check_failure('-1.0etest', 5, 'E474: Missing exponent: -1.0e')
|
||||
|
||||
@@ -5,13 +5,14 @@ local to_cstr = helpers.to_cstr
|
||||
local ffi = helpers.ffi
|
||||
local eq = helpers.eq
|
||||
|
||||
local eval = cimport('./src/nvim/eval.h', './src/nvim/eval_defs.h',
|
||||
local eval = cimport('./src/nvim/eval.h', './src/nvim/eval/typval.h',
|
||||
'./src/nvim/hashtab.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 locks_key = {[true]='locks key'}
|
||||
local list_type = {[true]='list type'}
|
||||
local dict_type = {[true]='dict type'}
|
||||
local func_type = {[true]='func type'}
|
||||
@@ -23,27 +24,71 @@ local nil_value = {[true]='nil'}
|
||||
local lua2typvalt
|
||||
|
||||
local function li_alloc(nogc)
|
||||
local gcfunc = eval.listitem_free
|
||||
local gcfunc = eval.tv_list_item_free
|
||||
if nogc then gcfunc = nil end
|
||||
local li = ffi.gc(eval.listitem_alloc(), gcfunc)
|
||||
local li = ffi.gc(eval.tv_list_item_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 li_tv = ffi.gc(lua2typvalt(val), nil)
|
||||
local li = li_alloc(true)
|
||||
li.li_tv = li_tv
|
||||
eval.tv_list_append(ret, li)
|
||||
local function populate_list(l, lua_l, processed)
|
||||
processed = processed or {}
|
||||
eq(0, l.lv_refcount)
|
||||
l.lv_refcount = 1
|
||||
processed[lua_l] = l
|
||||
for i = 1, #lua_l do
|
||||
local item_tv = ffi.gc(lua2typvalt(lua_l[i], processed), nil)
|
||||
local item_li = eval.tv_list_item_alloc()
|
||||
item_li.li_tv = item_tv
|
||||
eval.tv_list_append(l, item_li)
|
||||
end
|
||||
return ret
|
||||
return l
|
||||
end
|
||||
|
||||
local function populate_dict(d, lua_d, processed)
|
||||
processed = processed or {}
|
||||
eq(0, d.dv_refcount)
|
||||
d.dv_refcount = 1
|
||||
processed[lua_d] = d
|
||||
for k, v in pairs(lua_d) do
|
||||
if type(k) == 'string' then
|
||||
local di = eval.tv_dict_item_alloc(to_cstr(k))
|
||||
local val_tv = ffi.gc(lua2typvalt(v, processed), nil)
|
||||
eval.tv_copy(val_tv, di.di_tv)
|
||||
eval.tv_clear(val_tv)
|
||||
eval.tv_dict_add(d, di)
|
||||
end
|
||||
end
|
||||
return d
|
||||
end
|
||||
|
||||
local function populate_partial(pt, lua_pt, processed)
|
||||
processed = processed or {}
|
||||
eq(0, pt.pt_refcount)
|
||||
processed[lua_pt] = pt
|
||||
local argv = nil
|
||||
if lua_pt.args and #lua_pt.args > 0 then
|
||||
argv = ffi.gc(ffi.cast('typval_T*', eval.xmalloc(ffi.sizeof('typval_T') * #lua_pt.args)), nil)
|
||||
for i, arg in ipairs(lua_pt.args) do
|
||||
local arg_tv = ffi.gc(lua2typvalt(arg, processed), nil)
|
||||
argv[i - 1] = arg_tv
|
||||
end
|
||||
end
|
||||
local dict = nil
|
||||
if lua_pt.dict then
|
||||
local dict_tv = ffi.gc(lua2typvalt(lua_pt.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(lua_pt.value), #lua_pt.value)
|
||||
pt.pt_auto = not not lua_pt.auto
|
||||
pt.pt_argc = lua_pt.args and #lua_pt.args or 0
|
||||
pt.pt_argv = argv
|
||||
pt.pt_dict = dict
|
||||
return pt
|
||||
end
|
||||
|
||||
local ptr2key = function(ptr)
|
||||
@@ -54,6 +99,30 @@ local lst2tbl
|
||||
local dct2tbl
|
||||
|
||||
local typvalt2lua
|
||||
|
||||
local function partial2lua(pt, processed)
|
||||
processed = processed or {}
|
||||
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_tab = nil
|
||||
|
||||
local function typvalt2lua_tab_init()
|
||||
@@ -63,10 +132,10 @@ local function typvalt2lua_tab_init()
|
||||
typvalt2lua_tab = {
|
||||
[tonumber(eval.VAR_SPECIAL)] = function(t)
|
||||
return ({
|
||||
[eval.kSpecialVarFalse] = false,
|
||||
[eval.kSpecialVarNull] = nil_value,
|
||||
[eval.kSpecialVarTrue] = true,
|
||||
})[t.vval.v_special]
|
||||
[tonumber(eval.kSpecialVarFalse)] = false,
|
||||
[tonumber(eval.kSpecialVarNull)] = nil_value,
|
||||
[tonumber(eval.kSpecialVarTrue)] = true,
|
||||
})[tonumber(t.vval.v_special)]
|
||||
end,
|
||||
[tonumber(eval.VAR_NUMBER)] = function(t)
|
||||
return {[type_key]=int_type, value=tonumber(t.vval.v_number)}
|
||||
@@ -96,26 +165,7 @@ local function typvalt2lua_tab_init()
|
||||
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,
|
||||
}
|
||||
return partial2lua(t.vval.v_partial, processed)
|
||||
end,
|
||||
}
|
||||
end
|
||||
@@ -241,7 +291,7 @@ local typvalt = function(typ, vval)
|
||||
elseif type(typ) == 'string' then
|
||||
typ = eval[typ]
|
||||
end
|
||||
return ffi.gc(ffi.new('typval_T', {v_type=typ, vval=vval}), eval.clear_tv)
|
||||
return ffi.gc(ffi.new('typval_T', {v_type=typ, vval=vval}), eval.tv_clear)
|
||||
end
|
||||
|
||||
local lua2typvalt_type_tab = {
|
||||
@@ -256,36 +306,16 @@ local lua2typvalt_type_tab = {
|
||||
processed[l].lv_refcount = processed[l].lv_refcount + 1
|
||||
return typvalt(eval.VAR_LIST, {v_list=processed[l]})
|
||||
end
|
||||
local lst = eval.list_alloc()
|
||||
lst.lv_refcount = 1
|
||||
processed[l] = lst
|
||||
local ret = typvalt(eval.VAR_LIST, {v_list=lst})
|
||||
for i = 1, #l do
|
||||
local item_tv = ffi.gc(lua2typvalt(l[i], processed), nil)
|
||||
eval.list_append_tv(lst, item_tv)
|
||||
eval.clear_tv(item_tv)
|
||||
end
|
||||
return ret
|
||||
local lst = populate_list(eval.tv_list_alloc(), l, processed)
|
||||
return typvalt(eval.VAR_LIST, {v_list=lst})
|
||||
end,
|
||||
[dict_type] = function(l, processed)
|
||||
if processed[l] then
|
||||
processed[l].dv_refcount = processed[l].dv_refcount + 1
|
||||
return typvalt(eval.VAR_DICT, {v_dict=processed[l]})
|
||||
end
|
||||
local dct = eval.dict_alloc()
|
||||
dct.dv_refcount = 1
|
||||
processed[l] = dct
|
||||
local ret = typvalt(eval.VAR_DICT, {v_dict=dct})
|
||||
for k, v in pairs(l) do
|
||||
if type(k) == 'string' then
|
||||
local di = eval.dictitem_alloc(to_cstr(k))
|
||||
local val_tv = ffi.gc(lua2typvalt(v, processed), nil)
|
||||
eval.copy_tv(val_tv, di.di_tv)
|
||||
eval.clear_tv(val_tv)
|
||||
eval.dict_add(dct, di)
|
||||
end
|
||||
end
|
||||
return ret
|
||||
local dct = populate_dict(eval.tv_dict_alloc(), l, processed)
|
||||
return typvalt(eval.VAR_DICT, {v_dict=dct})
|
||||
end,
|
||||
[func_type] = function(l, processed)
|
||||
if processed[l] then
|
||||
@@ -293,29 +323,8 @@ local lua2typvalt_type_tab = {
|
||||
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
|
||||
local pt = populate_partial(ffi.gc(ffi.cast('partial_T*',
|
||||
eval.xcalloc(1, ffi.sizeof('partial_T'))), nil), l, processed)
|
||||
return typvalt(eval.VAR_PARTIAL, {v_partial=pt})
|
||||
else
|
||||
return typvalt(eval.VAR_FUNC, {
|
||||
@@ -368,13 +377,24 @@ lua2typvalt = function(l, processed)
|
||||
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.copy_tv(l, tv)
|
||||
eval.tv_copy(l, tv)
|
||||
return tv
|
||||
end
|
||||
end
|
||||
|
||||
local void_ptr = ffi.typeof('void *')
|
||||
local function void(ptr)
|
||||
return ffi.cast('void*', ptr)
|
||||
return ffi.cast(void_ptr, ptr)
|
||||
end
|
||||
|
||||
local function alloc_len(len, get_ptr)
|
||||
if type(len) == 'string' or type(len) == 'table' then
|
||||
return #len
|
||||
elseif len == nil then
|
||||
return eval.strlen(get_ptr())
|
||||
else
|
||||
return len
|
||||
end
|
||||
end
|
||||
|
||||
local alloc_logging_helpers = {
|
||||
@@ -382,14 +402,115 @@ local alloc_logging_helpers = {
|
||||
li = function(li) return {func='malloc', args={ffi.sizeof('listitem_T')}, ret=void(li)} end,
|
||||
dict = function(d) return {func='malloc', args={ffi.sizeof('dict_T')}, ret=void(d)} end,
|
||||
di = function(di, size)
|
||||
size = alloc_len(size, function() return di.di_key end)
|
||||
return {func='malloc', args={ffi.offsetof('dictitem_T', 'di_key') + size + 1}, ret=void(di)}
|
||||
end,
|
||||
str = function(s, size) return {func='malloc', args={size + 1}, ret=void(s)} end,
|
||||
str = function(s, size)
|
||||
size = alloc_len(size, function() return s end)
|
||||
return {func='malloc', args={size + 1}, ret=void(s)}
|
||||
end,
|
||||
|
||||
freed = function(p) return {func='free', args={p and void(p)}} end,
|
||||
dwatcher = function(w) return {func='malloc', args={ffi.sizeof('DictWatcher')}, ret=void(w)} end,
|
||||
|
||||
freed = function(p) return {func='free', args={type(p) == 'table' and p or void(p)}} end,
|
||||
|
||||
-- lua_…: allocated by this file, not by some Neovim function
|
||||
lua_pt = function(pt) return {func='calloc', args={1, ffi.sizeof('partial_T')}, ret=void(pt)} end,
|
||||
lua_tvs = function(argv, argc)
|
||||
argc = alloc_len(argc)
|
||||
return {func='malloc', args={ffi.sizeof('typval_T')*argc}, ret=void(argv)}
|
||||
end,
|
||||
}
|
||||
|
||||
local function int(n)
|
||||
return {[type_key]=int_type, value=n}
|
||||
end
|
||||
|
||||
local function list(...)
|
||||
return populate_list(ffi.gc(eval.tv_list_alloc(), eval.tv_list_unref),
|
||||
{...}, {})
|
||||
end
|
||||
|
||||
local function dict(d)
|
||||
return populate_dict(ffi.gc(eval.tv_dict_alloc(), eval.tv_dict_free),
|
||||
d or {}, {})
|
||||
end
|
||||
|
||||
local callback2tbl_type_tab = nil
|
||||
|
||||
local function init_callback2tbl_type_tab()
|
||||
if callback2tbl_type_tab then
|
||||
return
|
||||
end
|
||||
callback2tbl_type_tab = {
|
||||
[tonumber(eval.kCallbackNone)] = function(_) return {type='none'} end,
|
||||
[tonumber(eval.kCallbackFuncref)] = function(cb)
|
||||
return {type='fref', fref=ffi.string(cb.data.funcref)}
|
||||
end,
|
||||
[tonumber(eval.kCallbackPartial)] = function(cb)
|
||||
local lua_pt = partial2lua(cb.data.partial)
|
||||
return {type='pt', fref=ffi.string(lua_pt.value), pt=lua_pt}
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
local function callback2tbl(cb)
|
||||
init_callback2tbl_type_tab()
|
||||
return callback2tbl_type_tab[tonumber(cb.type)](cb)
|
||||
end
|
||||
|
||||
local function tbl2callback(tbl)
|
||||
local ret = nil
|
||||
if tbl.type == 'none' then
|
||||
ret = ffi.new('Callback[1]', {{type=eval.kCallbackNone}})
|
||||
elseif tbl.type == 'fref' then
|
||||
ret = ffi.new('Callback[1]', {{type=eval.kCallbackFuncref,
|
||||
data={funcref=eval.xstrdup(tbl.fref)}}})
|
||||
elseif tbl.type == 'pt' then
|
||||
local pt = ffi.gc(ffi.cast('partial_T*',
|
||||
eval.xcalloc(1, ffi.sizeof('partial_T'))), nil)
|
||||
ret = ffi.new('Callback[1]', {{type=eval.kCallbackPartial,
|
||||
data={partial=populate_partial(pt, tbl.pt, {})}}})
|
||||
else
|
||||
assert(false)
|
||||
end
|
||||
return ffi.gc(ffi.cast('Callback*', ret), helpers.callback_free)
|
||||
end
|
||||
|
||||
local function dict_watchers(d)
|
||||
local ret = {}
|
||||
local h = d.watchers
|
||||
local q = h.next
|
||||
local qs = {}
|
||||
local key_patterns = {}
|
||||
while q ~= h do
|
||||
local qitem = ffi.cast('DictWatcher *',
|
||||
ffi.cast('char *', q) - ffi.offsetof('DictWatcher', 'node'))
|
||||
ret[#ret + 1] = {
|
||||
cb=callback2tbl(qitem.callback),
|
||||
pat=ffi.string(qitem.key_pattern, qitem.key_pattern_len),
|
||||
busy=qitem.busy,
|
||||
}
|
||||
qs[#qs + 1] = qitem
|
||||
key_patterns[#key_patterns + 1] = {qitem.key_pattern, qitem.key_pattern_len}
|
||||
q = q.next
|
||||
end
|
||||
return ret, qs, key_patterns
|
||||
end
|
||||
|
||||
local function eval0(expr)
|
||||
local tv = ffi.gc(ffi.new('typval_T', {v_type=eval.VAR_UNKNOWN}),
|
||||
eval.tv_clear)
|
||||
if eval.eval0(to_cstr(expr), tv, nil, true) == 0 then
|
||||
return nil
|
||||
else
|
||||
return tv
|
||||
end
|
||||
end
|
||||
|
||||
return {
|
||||
int=int,
|
||||
|
||||
null_string=null_string,
|
||||
null_list=null_list,
|
||||
null_dict=null_dict,
|
||||
@@ -402,8 +523,10 @@ return {
|
||||
nil_value=nil_value,
|
||||
|
||||
type_key=type_key,
|
||||
locks_key=locks_key,
|
||||
|
||||
list=list,
|
||||
dict=dict,
|
||||
lst2tbl=lst2tbl,
|
||||
dct2tbl=dct2tbl,
|
||||
|
||||
@@ -422,4 +545,12 @@ return {
|
||||
|
||||
list_items=list_items,
|
||||
dict_items=dict_items,
|
||||
|
||||
dict_watchers=dict_watchers,
|
||||
tbl2callback=tbl2callback,
|
||||
callback2tbl=callback2tbl,
|
||||
|
||||
eval0=eval0,
|
||||
|
||||
empty_list = {[type_key]=list_type},
|
||||
}
|
||||
|
||||
@@ -1,19 +1,15 @@
|
||||
local helpers = require('test.unit.helpers')(after_each)
|
||||
local eval_helpers = require('test.unit.eval.helpers')
|
||||
|
||||
local itp = helpers.gen_itp(it)
|
||||
|
||||
local cimport = helpers.cimport
|
||||
local to_cstr = helpers.to_cstr
|
||||
local ffi = helpers.ffi
|
||||
local eq = helpers.eq
|
||||
|
||||
local eval = cimport('./src/nvim/eval.h', './src/nvim/memory.h')
|
||||
local eval0 = eval_helpers.eval0
|
||||
|
||||
local eval_expr = function(expr)
|
||||
return ffi.gc(eval.eval_expr(to_cstr(expr), nil), function(tv)
|
||||
eval.clear_tv(tv)
|
||||
eval.xfree(tv)
|
||||
end)
|
||||
end
|
||||
local eval = cimport('./src/nvim/eval.h', './src/nvim/eval/typval.h',
|
||||
'./src/nvim/memory.h')
|
||||
|
||||
describe('NULL typval_T', function()
|
||||
itp('is produced by $XXX_UNEXISTENT_VAR_XXX', function()
|
||||
@@ -25,19 +21,19 @@ describe('NULL typval_T', function()
|
||||
while os.getenv(unexistent_env) ~= nil do
|
||||
unexistent_env = unexistent_env .. '_XXX'
|
||||
end
|
||||
local rettv = eval_expr('$' .. unexistent_env)
|
||||
local rettv = eval0('$' .. unexistent_env)
|
||||
eq(eval.VAR_STRING, rettv.v_type)
|
||||
eq(nil, rettv.vval.v_string)
|
||||
end)
|
||||
|
||||
itp('is produced by v:_null_list', function()
|
||||
local rettv = eval_expr('v:_null_list')
|
||||
local rettv = eval0('v:_null_list')
|
||||
eq(eval.VAR_LIST, rettv.v_type)
|
||||
eq(nil, rettv.vval.v_list)
|
||||
end)
|
||||
|
||||
itp('is produced by v:_null_dict', function()
|
||||
local rettv = eval_expr('v:_null_dict')
|
||||
local rettv = eval0('v:_null_dict')
|
||||
eq(eval.VAR_DICT, rettv.v_type)
|
||||
eq(nil, rettv.vval.v_dict)
|
||||
end)
|
||||
|
||||
@@ -14,7 +14,7 @@ local list_items = eval_helpers.list_items
|
||||
local dict_items = eval_helpers.dict_items
|
||||
local lua2typvalt = eval_helpers.lua2typvalt
|
||||
|
||||
local lib = cimport('./src/nvim/eval_defs.h', './src/nvim/eval.h')
|
||||
local lib = cimport('./src/nvim/eval/typval.h', './src/nvim/eval.h')
|
||||
|
||||
local alloc_log = alloc_log_new()
|
||||
|
||||
@@ -26,7 +26,7 @@ after_each(function()
|
||||
alloc_log:after_each()
|
||||
end)
|
||||
|
||||
describe('clear_tv()', function()
|
||||
describe('tv_clear()', function()
|
||||
itp('successfully frees all lists in [&l [1], *l, *l]', function()
|
||||
local l_inner = {1}
|
||||
local list = {l_inner, l_inner, l_inner}
|
||||
@@ -44,7 +44,7 @@ describe('clear_tv()', function()
|
||||
a.li(lis[3]),
|
||||
})
|
||||
eq(3, list_inner_p.lv_refcount)
|
||||
lib.clear_tv(list_tv)
|
||||
lib.tv_clear(list_tv)
|
||||
alloc_log:check({
|
||||
a.freed(lis_inner[1]),
|
||||
a.freed(list_inner_p),
|
||||
@@ -69,7 +69,7 @@ describe('clear_tv()', function()
|
||||
a.li(lis[3]),
|
||||
})
|
||||
eq(3, list_inner_p.lv_refcount)
|
||||
lib.clear_tv(list_tv)
|
||||
lib.tv_clear(list_tv)
|
||||
alloc_log:check({
|
||||
a.freed(list_inner_p),
|
||||
a.freed(lis[1]),
|
||||
@@ -92,7 +92,7 @@ describe('clear_tv()', function()
|
||||
a.li(lis[2]),
|
||||
})
|
||||
eq(2, dict_inner_p.dv_refcount)
|
||||
lib.clear_tv(list_tv)
|
||||
lib.tv_clear(list_tv)
|
||||
alloc_log:check({
|
||||
a.freed(dict_inner_p),
|
||||
a.freed(lis[1]),
|
||||
@@ -116,7 +116,7 @@ describe('clear_tv()', function()
|
||||
a.li(lis[2]),
|
||||
})
|
||||
eq(2, dict_inner_p.dv_refcount)
|
||||
lib.clear_tv(list_tv)
|
||||
lib.tv_clear(list_tv)
|
||||
alloc_log:check({
|
||||
a.freed(dis.a),
|
||||
a.freed(dict_inner_p),
|
||||
|
||||
3049
test/unit/eval/typval_spec.lua
Normal file
3049
test/unit/eval/typval_spec.lua
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,6 @@
|
||||
// This is an open source non-commercial project. Dear PVS-Studio, please check
|
||||
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "nvim/event/multiqueue.h"
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// This is an open source non-commercial project. Dear PVS-Studio, please check
|
||||
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
|
||||
|
||||
#include "nvim/rbuffer.h"
|
||||
#include "rbuffer.h"
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ local posix = nil
|
||||
local syscall = nil
|
||||
|
||||
local check_cores = global_helpers.check_cores
|
||||
local dedent = global_helpers.dedent
|
||||
local neq = global_helpers.neq
|
||||
local map = global_helpers.map
|
||||
local eq = global_helpers.eq
|
||||
@@ -137,6 +138,7 @@ local function filter_complex_blocks(body)
|
||||
for line in body:gmatch("[^\r\n]+") do
|
||||
if not (string.find(line, "(^)", 1, true) ~= nil
|
||||
or string.find(line, "_ISwupper", 1, true)
|
||||
or string.find(line, "_Float128")
|
||||
or string.find(line, "msgpack_zone_push_finalizer")
|
||||
or string.find(line, "msgpack_unpacker_reserve_buffer")
|
||||
or string.find(line, "UUID_NULL") -- static const uuid_t UUID_NULL = {...}
|
||||
@@ -314,6 +316,29 @@ local function alloc_log_new()
|
||||
eq(exp, self.log)
|
||||
self:clear()
|
||||
end
|
||||
function log:clear_tmp_allocs()
|
||||
local toremove = {}
|
||||
local allocs = {}
|
||||
for i, v in ipairs(self.log) do
|
||||
if v.func == 'malloc' or v.func == 'calloc' then
|
||||
allocs[tostring(v.ret)] = i
|
||||
elseif v.func == 'realloc' or v.func == 'free' then
|
||||
if allocs[tostring(v.args[1])] then
|
||||
toremove[#toremove + 1] = allocs[tostring(v.args[1])]
|
||||
if v.func == 'free' then
|
||||
toremove[#toremove + 1] = i
|
||||
end
|
||||
end
|
||||
if v.func == 'realloc' then
|
||||
allocs[tostring(v.ret)] = i
|
||||
end
|
||||
end
|
||||
end
|
||||
table.sort(toremove)
|
||||
for i = #toremove,1,-1 do
|
||||
table.remove(self.log, toremove[i])
|
||||
end
|
||||
end
|
||||
function log:restore_original_functions()
|
||||
-- Do nothing: set mocks live in a separate process
|
||||
return
|
||||
@@ -488,6 +513,212 @@ if os.getenv('NVIM_TEST_PRINT_SYSCALLS') == '1' then
|
||||
end
|
||||
end
|
||||
|
||||
local function just_fail(_)
|
||||
return false
|
||||
end
|
||||
say:set('assertion.just_fail.positive', '%s')
|
||||
say:set('assertion.just_fail.negative', '%s')
|
||||
assert:register('assertion', 'just_fail', just_fail,
|
||||
'assertion.just_fail.positive',
|
||||
'assertion.just_fail.negative')
|
||||
|
||||
local hook_fnamelen = 30
|
||||
local hook_sfnamelen = 30
|
||||
local hook_numlen = 5
|
||||
local hook_msglen = 1 + 1 + 1 + (1 + hook_fnamelen) + (1 + hook_sfnamelen) + (1 + hook_numlen) + 1
|
||||
|
||||
local tracehelp = dedent([[
|
||||
┌ Trace type: _r_eturn from function , function _c_all, _l_ine executed,
|
||||
│ _t_ail return, _C_ount (should not actually appear),
|
||||
│ _s_aved from previous run for reference.
|
||||
│┏ Function type: _L_ua function, _C_ function, _m_ain part of chunk,
|
||||
│┃ function that did _t_ail call.
|
||||
│┃┌ Function name type: _g_lobal, _l_ocal, _m_ethod, _f_ield, _u_pvalue,
|
||||
│┃│ space for unknown.
|
||||
│┃│ ┏ Source file name ┌ Function name ┏ Line
|
||||
│┃│ ┃ (trunc to 30 bytes, no .lua) │ (truncated to last 30 bytes) ┃ number
|
||||
CWN SSSSSSSSSSSSSSSSSSSSSSSSSSSSSS:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:LLLLL\n
|
||||
]])
|
||||
|
||||
local function child_sethook(wr)
|
||||
local trace_level = os.getenv('NVIM_TEST_TRACE_LEVEL')
|
||||
if not trace_level or trace_level == '' then
|
||||
trace_level = 1
|
||||
else
|
||||
trace_level = tonumber(trace_level)
|
||||
end
|
||||
if trace_level <= 0 then
|
||||
return
|
||||
end
|
||||
local trace_only_c = trace_level <= 1
|
||||
local prev_info, prev_reason, prev_lnum
|
||||
local function hook(reason, lnum, use_prev)
|
||||
local info = nil
|
||||
if use_prev then
|
||||
info = prev_info
|
||||
elseif reason ~= 'tail return' then -- tail return
|
||||
info = debug.getinfo(2, 'nSl')
|
||||
end
|
||||
|
||||
if trace_only_c and (not info or info.what ~= 'C') and not use_prev then
|
||||
if info.source:sub(-9) == '_spec.lua' then
|
||||
prev_info = info
|
||||
prev_reason = 'saved'
|
||||
prev_lnum = lnum
|
||||
end
|
||||
return
|
||||
end
|
||||
if trace_only_c and not use_prev and prev_reason then
|
||||
hook(prev_reason, prev_lnum, true)
|
||||
prev_reason = nil
|
||||
end
|
||||
|
||||
local whatchar = ' '
|
||||
local namewhatchar = ' '
|
||||
local funcname = ''
|
||||
local source = ''
|
||||
local msgchar = reason:sub(1, 1)
|
||||
|
||||
if reason == 'count' then
|
||||
msgchar = 'C'
|
||||
end
|
||||
|
||||
if info then
|
||||
funcname = (info.name or ''):sub(1, hook_fnamelen)
|
||||
whatchar = info.what:sub(1, 1)
|
||||
namewhatchar = info.namewhat:sub(1, 1)
|
||||
if namewhatchar == '' then
|
||||
namewhatchar = ' '
|
||||
end
|
||||
source = info.source
|
||||
if source:sub(1, 1) == '@' then
|
||||
if source:sub(-4, -1) == '.lua' then
|
||||
source = source:sub(1, -5)
|
||||
end
|
||||
source = source:sub(-hook_sfnamelen, -1)
|
||||
end
|
||||
lnum = lnum or info.currentline
|
||||
end
|
||||
|
||||
-- assert(-1 <= lnum and lnum <= 99999)
|
||||
local lnum_s
|
||||
if lnum == -1 then
|
||||
lnum_s = 'nknwn'
|
||||
else
|
||||
lnum_s = ('%u'):format(lnum)
|
||||
end
|
||||
local msg = ( -- lua does not support %*
|
||||
''
|
||||
.. msgchar
|
||||
.. whatchar
|
||||
.. namewhatchar
|
||||
.. ' '
|
||||
.. source .. (' '):rep(hook_sfnamelen - #source)
|
||||
.. ':'
|
||||
.. funcname .. (' '):rep(hook_fnamelen - #funcname)
|
||||
.. ':'
|
||||
.. ('0'):rep(hook_numlen - #lnum_s) .. lnum_s
|
||||
.. '\n'
|
||||
)
|
||||
-- eq(hook_msglen, #msg)
|
||||
sc.write(wr, msg)
|
||||
end
|
||||
debug.sethook(hook, 'crl')
|
||||
end
|
||||
|
||||
local trace_end_msg = ('E%s\n'):format((' '):rep(hook_msglen - 2))
|
||||
|
||||
local function itp_child(wr, func)
|
||||
init()
|
||||
collectgarbage('stop')
|
||||
child_sethook(wr)
|
||||
local err, emsg = pcall(func)
|
||||
collectgarbage('restart')
|
||||
collectgarbage()
|
||||
debug.sethook()
|
||||
emsg = tostring(emsg)
|
||||
sc.write(wr, trace_end_msg)
|
||||
if not err then
|
||||
if #emsg > 99999 then
|
||||
emsg = emsg:sub(1, 99999)
|
||||
end
|
||||
sc.write(wr, ('-\n%05u\n%s'):format(#emsg, emsg))
|
||||
deinit()
|
||||
sc.close(wr)
|
||||
sc.exit(1)
|
||||
else
|
||||
sc.write(wr, '+\n')
|
||||
deinit()
|
||||
sc.close(wr)
|
||||
sc.exit(0)
|
||||
end
|
||||
end
|
||||
|
||||
local function check_child_err(rd)
|
||||
local trace = {}
|
||||
local did_traceline = false
|
||||
while true do
|
||||
local traceline = sc.read(rd, hook_msglen)
|
||||
if #traceline ~= hook_msglen then
|
||||
if #traceline == 0 then
|
||||
break
|
||||
else
|
||||
trace[#trace + 1] = 'Partial read: <' .. trace .. '>\n'
|
||||
end
|
||||
end
|
||||
if traceline == trace_end_msg then
|
||||
did_traceline = true
|
||||
break
|
||||
end
|
||||
trace[#trace + 1] = traceline
|
||||
end
|
||||
local res = sc.read(rd, 2)
|
||||
if #res ~= 2 then
|
||||
local error
|
||||
if #trace == 0 then
|
||||
error = '\nTest crashed, no trace available\n'
|
||||
else
|
||||
error = '\nTest crashed, trace:\n' .. tracehelp
|
||||
for i = 1, #trace do
|
||||
error = error .. trace[i]
|
||||
end
|
||||
end
|
||||
if not did_traceline then
|
||||
error = error .. '\nNo end of trace occurred'
|
||||
end
|
||||
local cc_err, cc_emsg = pcall(check_cores, Paths.test_luajit_prg, true)
|
||||
if not cc_err then
|
||||
error = error .. '\ncheck_cores failed: ' .. cc_emsg
|
||||
end
|
||||
assert.just_fail(error)
|
||||
end
|
||||
if res == '+\n' then
|
||||
return
|
||||
end
|
||||
eq('-\n', res)
|
||||
local len_s = sc.read(rd, 5)
|
||||
local len = tonumber(len_s)
|
||||
neq(0, len)
|
||||
local err = sc.read(rd, len + 1)
|
||||
assert.just_fail(err)
|
||||
end
|
||||
|
||||
local function itp_parent(rd, pid, allow_failure)
|
||||
local err, emsg = pcall(check_child_err, rd)
|
||||
sc.wait(pid)
|
||||
sc.close(rd)
|
||||
if not err then
|
||||
if allow_failure then
|
||||
io.stderr:write('Errorred out:\n' .. tostring(emsg) .. '\n')
|
||||
os.execute([[
|
||||
sh -c "source ci/common/test.sh
|
||||
check_core_dumps --delete \"]] .. Paths.test_luajit_prg .. [[\""]])
|
||||
else
|
||||
error(emsg)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function gen_itp(it)
|
||||
child_calls_mod = {}
|
||||
child_calls_mod_once = {}
|
||||
@@ -495,14 +726,6 @@ local function gen_itp(it)
|
||||
preprocess_cache_mod = map(function(v) return v end, preprocess_cache_init)
|
||||
previous_defines_mod = previous_defines_init
|
||||
cdefs_mod = cdefs_init:copy()
|
||||
local function just_fail(_)
|
||||
return false
|
||||
end
|
||||
say:set('assertion.just_fail.positive', '%s')
|
||||
say:set('assertion.just_fail.negative', '%s')
|
||||
assert:register('assertion', 'just_fail', just_fail,
|
||||
'assertion.just_fail.positive',
|
||||
'assertion.just_fail.negative')
|
||||
local function itp(name, func, allow_failure)
|
||||
if allow_failure and os.getenv('NVIM_TEST_RUN_FAILING_TESTS') ~= '1' then
|
||||
-- FIXME Fix tests with this true
|
||||
@@ -512,50 +735,13 @@ local function gen_itp(it)
|
||||
local rd, wr = sc.pipe()
|
||||
child_pid = sc.fork()
|
||||
if child_pid == 0 then
|
||||
init()
|
||||
sc.close(rd)
|
||||
collectgarbage('stop')
|
||||
local err, emsg = pcall(func)
|
||||
collectgarbage('restart')
|
||||
emsg = tostring(emsg)
|
||||
if not err then
|
||||
sc.write(wr, ('-\n%05u\n%s'):format(#emsg, emsg))
|
||||
deinit()
|
||||
sc.close(wr)
|
||||
sc.exit(1)
|
||||
else
|
||||
sc.write(wr, '+\n')
|
||||
deinit()
|
||||
sc.close(wr)
|
||||
sc.exit(0)
|
||||
end
|
||||
itp_child(wr, func)
|
||||
else
|
||||
sc.close(wr)
|
||||
sc.wait(child_pid)
|
||||
local saved_child_pid = child_pid
|
||||
child_pid = nil
|
||||
local function check()
|
||||
local res = sc.read(rd, 2)
|
||||
eq(2, #res)
|
||||
if res == '+\n' then
|
||||
return
|
||||
end
|
||||
eq('-\n', res)
|
||||
local len_s = sc.read(rd, 5)
|
||||
local len = tonumber(len_s)
|
||||
neq(0, len)
|
||||
local err = sc.read(rd, len + 1)
|
||||
assert.just_fail(err)
|
||||
end
|
||||
local err, emsg = pcall(check)
|
||||
sc.close(rd)
|
||||
if not err then
|
||||
if allow_failure then
|
||||
io.stderr:write('Errorred out:\n' .. tostring(emsg) .. '\n')
|
||||
os.execute([[sh -c "source .ci/common/test.sh ; check_core_dumps --delete \"]] .. Paths.test_luajit_prg .. [[\""]])
|
||||
else
|
||||
error(emsg)
|
||||
end
|
||||
end
|
||||
itp_parent(rd, saved_child_pid, allow_failure)
|
||||
end
|
||||
end)
|
||||
end
|
||||
@@ -563,7 +749,7 @@ local function gen_itp(it)
|
||||
end
|
||||
|
||||
local function cppimport(path)
|
||||
return cimport(Paths.test_include_path .. '/' .. path)
|
||||
return cimport(Paths.test_source_path .. '/test/includes/pre/' .. path)
|
||||
end
|
||||
|
||||
cimport('./src/nvim/types.h', './src/nvim/main.h', './src/nvim/os/time.h')
|
||||
@@ -587,12 +773,8 @@ local module = {
|
||||
only_separate = only_separate,
|
||||
child_call_once = child_call_once,
|
||||
child_cleanup_once = child_cleanup_once,
|
||||
sc = sc,
|
||||
}
|
||||
return function(after_each)
|
||||
if after_each then
|
||||
after_each(function()
|
||||
check_cores(Paths.test_luajit_prg)
|
||||
end)
|
||||
end
|
||||
return function()
|
||||
return module
|
||||
end
|
||||
|
||||
@@ -13,7 +13,7 @@ require('lfs')
|
||||
|
||||
local cimp = cimport('./src/nvim/os/os.h')
|
||||
|
||||
describe('env function', function()
|
||||
describe('env.c', function()
|
||||
local function os_setenv(name, value, override)
|
||||
return cimp.os_setenv(to_cstr(name), to_cstr(value), override)
|
||||
end
|
||||
@@ -35,17 +35,17 @@ describe('env function', function()
|
||||
local OK = 0
|
||||
|
||||
itp('sets an env variable and returns OK', function()
|
||||
local name = 'NEOVIM_UNIT_TEST_SETENV_1N'
|
||||
local value = 'NEOVIM_UNIT_TEST_SETENV_1V'
|
||||
local name = 'NVIM_UNIT_TEST_SETENV_1N'
|
||||
local value = 'NVIM_UNIT_TEST_SETENV_1V'
|
||||
eq(nil, os.getenv(name))
|
||||
eq(OK, (os_setenv(name, value, 1)))
|
||||
eq(value, os.getenv(name))
|
||||
end)
|
||||
|
||||
itp("dosn't overwrite an env variable if overwrite is 0", function()
|
||||
local name = 'NEOVIM_UNIT_TEST_SETENV_2N'
|
||||
local value = 'NEOVIM_UNIT_TEST_SETENV_2V'
|
||||
local value_updated = 'NEOVIM_UNIT_TEST_SETENV_2V_UPDATED'
|
||||
local name = 'NVIM_UNIT_TEST_SETENV_2N'
|
||||
local value = 'NVIM_UNIT_TEST_SETENV_2V'
|
||||
local value_updated = 'NVIM_UNIT_TEST_SETENV_2V_UPDATED'
|
||||
eq(OK, (os_setenv(name, value, 0)))
|
||||
eq(value, os.getenv(name))
|
||||
eq(OK, (os_setenv(name, value_updated, 0)))
|
||||
@@ -67,18 +67,43 @@ describe('env function', function()
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('os_shell_is_cmdexe', function()
|
||||
itp('returns true for expected names', function()
|
||||
eq(true, cimp.os_shell_is_cmdexe(to_cstr('cmd.exe')))
|
||||
eq(true, cimp.os_shell_is_cmdexe(to_cstr('cmd')))
|
||||
eq(true, cimp.os_shell_is_cmdexe(to_cstr('CMD.EXE')))
|
||||
eq(true, cimp.os_shell_is_cmdexe(to_cstr('CMD')))
|
||||
|
||||
os_setenv('COMSPEC', '/foo/bar/cmd.exe', 0)
|
||||
eq(true, cimp.os_shell_is_cmdexe(to_cstr('$COMSPEC')))
|
||||
os_setenv('COMSPEC', [[C:\system32\cmd.exe]], 0)
|
||||
eq(true, cimp.os_shell_is_cmdexe(to_cstr('$COMSPEC')))
|
||||
end)
|
||||
itp('returns false for unexpected names', function()
|
||||
eq(false, cimp.os_shell_is_cmdexe(to_cstr('')))
|
||||
eq(false, cimp.os_shell_is_cmdexe(to_cstr('powershell')))
|
||||
eq(false, cimp.os_shell_is_cmdexe(to_cstr(' cmd.exe ')))
|
||||
eq(false, cimp.os_shell_is_cmdexe(to_cstr('cm')))
|
||||
eq(false, cimp.os_shell_is_cmdexe(to_cstr('md')))
|
||||
eq(false, cimp.os_shell_is_cmdexe(to_cstr('cmd.ex')))
|
||||
|
||||
os_setenv('COMSPEC', '/foo/bar/cmd', 0)
|
||||
eq(false, cimp.os_shell_is_cmdexe(to_cstr('$COMSPEC')))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('os_getenv', function()
|
||||
itp('reads an env variable', function()
|
||||
local name = 'NEOVIM_UNIT_TEST_GETENV_1N'
|
||||
local value = 'NEOVIM_UNIT_TEST_GETENV_1V'
|
||||
local name = 'NVIM_UNIT_TEST_GETENV_1N'
|
||||
local value = 'NVIM_UNIT_TEST_GETENV_1V'
|
||||
eq(NULL, os_getenv(name))
|
||||
-- need to use os_setenv, because lua dosn't have a setenv function
|
||||
-- Use os_setenv because Lua dosen't have setenv.
|
||||
os_setenv(name, value, 1)
|
||||
eq(value, os_getenv(name))
|
||||
end)
|
||||
|
||||
itp('returns NULL if the env variable is not found', function()
|
||||
local name = 'NEOVIM_UNIT_TEST_GETENV_NOTFOUND'
|
||||
local name = 'NVIM_UNIT_TEST_GETENV_NOTFOUND'
|
||||
return eq(NULL, os_getenv(name))
|
||||
end)
|
||||
end)
|
||||
@@ -97,8 +122,8 @@ describe('env function', function()
|
||||
|
||||
describe('os_getenvname_at_index', function()
|
||||
itp('returns names of environment variables', function()
|
||||
local test_name = 'NEOVIM_UNIT_TEST_GETENVNAME_AT_INDEX_1N'
|
||||
local test_value = 'NEOVIM_UNIT_TEST_GETENVNAME_AT_INDEX_1V'
|
||||
local test_name = 'NVIM_UNIT_TEST_GETENVNAME_AT_INDEX_1N'
|
||||
local test_value = 'NVIM_UNIT_TEST_GETENVNAME_AT_INDEX_1V'
|
||||
os_setenv(test_name, test_value, 1)
|
||||
local i = 0
|
||||
local names = { }
|
||||
@@ -160,16 +185,16 @@ describe('env function', function()
|
||||
|
||||
describe('expand_env_esc', function()
|
||||
itp('expands environment variables', function()
|
||||
local name = 'NEOVIM_UNIT_TEST_EXPAND_ENV_ESCN'
|
||||
local value = 'NEOVIM_UNIT_TEST_EXPAND_ENV_ESCV'
|
||||
local name = 'NVIM_UNIT_TEST_EXPAND_ENV_ESCN'
|
||||
local value = 'NVIM_UNIT_TEST_EXPAND_ENV_ESCV'
|
||||
os_setenv(name, value, 1)
|
||||
-- TODO(bobtwinkles) This only tests Unix expansions. There should be a
|
||||
-- test for Windows as well
|
||||
local input1 = to_cstr('$NEOVIM_UNIT_TEST_EXPAND_ENV_ESCN/test')
|
||||
local input2 = to_cstr('${NEOVIM_UNIT_TEST_EXPAND_ENV_ESCN}/test')
|
||||
local input1 = to_cstr('$NVIM_UNIT_TEST_EXPAND_ENV_ESCN/test')
|
||||
local input2 = to_cstr('${NVIM_UNIT_TEST_EXPAND_ENV_ESCN}/test')
|
||||
local output_buff1 = cstr(255, '')
|
||||
local output_buff2 = cstr(255, '')
|
||||
local output_expected = 'NEOVIM_UNIT_TEST_EXPAND_ENV_ESCV/test'
|
||||
local output_expected = 'NVIM_UNIT_TEST_EXPAND_ENV_ESCV/test'
|
||||
cimp.expand_env_esc(input1, output_buff1, 255, false, true, NULL)
|
||||
cimp.expand_env_esc(input2, output_buff2, 255, false, true, NULL)
|
||||
eq(output_expected, ffi.string(output_buff1))
|
||||
@@ -204,10 +229,10 @@ describe('env function', function()
|
||||
|
||||
local src = to_cstr("~"..curuser.."/Vcs/django-rest-framework/rest_framework/renderers.py")
|
||||
local dst = cstr(256, "~"..curuser)
|
||||
cimp.expand_env_esc(src, dst, 1024, false, false, NULL)
|
||||
cimp.expand_env_esc(src, dst, 256, false, false, NULL)
|
||||
local len = string.len(ffi.string(dst))
|
||||
assert.True(len > 56)
|
||||
assert.True(len < 99)
|
||||
assert.True(len < 256)
|
||||
end)
|
||||
|
||||
itp('respects `dstlen` without expansion', function()
|
||||
|
||||
@@ -60,15 +60,15 @@ local function file_open_new(fname, flags, mode)
|
||||
return ret1[0], ret2
|
||||
end
|
||||
|
||||
local function file_open_fd(fd, flags, mode)
|
||||
local function file_open_fd(fd, flags)
|
||||
local ret2 = ffi.new('FileDescriptor')
|
||||
local ret1 = m.file_open_fd(ret2, fd, flags, mode)
|
||||
local ret1 = m.file_open_fd(ret2, fd, flags)
|
||||
return ret1, ret2
|
||||
end
|
||||
|
||||
local function file_open_fd_new(fd, flags, mode)
|
||||
local function file_open_fd_new(fd, flags)
|
||||
local ret1 = ffi.new('int[?]', 1, {0})
|
||||
local ret2 = ffi.gc(m.file_open_fd_new(ret1, fd, flags, mode), nil)
|
||||
local ret2 = ffi.gc(m.file_open_fd_new(ret1, fd, flags), nil)
|
||||
return ret1[0], ret2
|
||||
end
|
||||
|
||||
@@ -76,6 +76,10 @@ local function file_write(fp, buf)
|
||||
return m.file_write(fp, buf, #buf)
|
||||
end
|
||||
|
||||
local function msgpack_file_write(fp, buf)
|
||||
return m.msgpack_file_write(fp, buf, #buf)
|
||||
end
|
||||
|
||||
local function file_read(fp, size)
|
||||
local buf = nil
|
||||
if size == nil then
|
||||
@@ -94,6 +98,10 @@ local function file_read(fp, size)
|
||||
return ret1, ret2
|
||||
end
|
||||
|
||||
local function file_flush(fp)
|
||||
return m.file_flush(fp)
|
||||
end
|
||||
|
||||
local function file_fsync(fp)
|
||||
return m.file_fsync(fp)
|
||||
end
|
||||
@@ -105,18 +113,18 @@ end
|
||||
describe('file_open_fd', function()
|
||||
itp('can use file descriptor returned by os_open for reading', function()
|
||||
local fd = m.os_open(file1, m.kO_RDONLY, 0)
|
||||
local err, fp = file_open_fd(fd, m.kFileReadOnly, 0)
|
||||
local err, fp = file_open_fd(fd, m.kFileReadOnly)
|
||||
eq(0, err)
|
||||
eq({#fcontents, fcontents}, {file_read(fp, #fcontents)})
|
||||
eq(0, m.file_close(fp))
|
||||
eq(0, m.file_close(fp, false))
|
||||
end)
|
||||
itp('can use file descriptor returned by os_open for writing', function()
|
||||
eq(nil, lfs.attributes(filec))
|
||||
local fd = m.os_open(filec, m.kO_WRONLY + m.kO_CREAT, 384)
|
||||
local err, fp = file_open_fd(fd, m.kFileWriteOnly, 0)
|
||||
local err, fp = file_open_fd(fd, m.kFileWriteOnly)
|
||||
eq(0, err)
|
||||
eq(4, file_write(fp, 'test'))
|
||||
eq(0, m.file_close(fp))
|
||||
eq(0, m.file_close(fp, false))
|
||||
eq(4, lfs.attributes(filec).size)
|
||||
eq('test', io.open(filec):read('*a'))
|
||||
end)
|
||||
@@ -125,18 +133,18 @@ end)
|
||||
describe('file_open_fd_new', function()
|
||||
itp('can use file descriptor returned by os_open for reading', function()
|
||||
local fd = m.os_open(file1, m.kO_RDONLY, 0)
|
||||
local err, fp = file_open_fd_new(fd, m.kFileReadOnly, 0)
|
||||
local err, fp = file_open_fd_new(fd, m.kFileReadOnly)
|
||||
eq(0, err)
|
||||
eq({#fcontents, fcontents}, {file_read(fp, #fcontents)})
|
||||
eq(0, m.file_free(fp))
|
||||
eq(0, m.file_free(fp, false))
|
||||
end)
|
||||
itp('can use file descriptor returned by os_open for writing', function()
|
||||
eq(nil, lfs.attributes(filec))
|
||||
local fd = m.os_open(filec, m.kO_WRONLY + m.kO_CREAT, 384)
|
||||
local err, fp = file_open_fd_new(fd, m.kFileWriteOnly, 0)
|
||||
local err, fp = file_open_fd_new(fd, m.kFileWriteOnly)
|
||||
eq(0, err)
|
||||
eq(4, file_write(fp, 'test'))
|
||||
eq(0, m.file_free(fp))
|
||||
eq(0, m.file_free(fp, false))
|
||||
eq(4, lfs.attributes(filec).size)
|
||||
eq('test', io.open(filec):read('*a'))
|
||||
end)
|
||||
@@ -148,7 +156,7 @@ describe('file_open', function()
|
||||
eq(0, err)
|
||||
local attrs = lfs.attributes(filec)
|
||||
eq('rwx------', attrs.permissions)
|
||||
eq(0, m.file_close(fp))
|
||||
eq(0, m.file_close(fp, false))
|
||||
end)
|
||||
|
||||
itp('can create a rw------- file with kFileCreate', function()
|
||||
@@ -156,7 +164,7 @@ describe('file_open', function()
|
||||
eq(0, err)
|
||||
local attrs = lfs.attributes(filec)
|
||||
eq('rw-------', attrs.permissions)
|
||||
eq(0, m.file_close(fp))
|
||||
eq(0, m.file_close(fp, false))
|
||||
end)
|
||||
|
||||
itp('can create a rwx------ file with kFileCreateOnly', function()
|
||||
@@ -164,7 +172,7 @@ describe('file_open', function()
|
||||
eq(0, err)
|
||||
local attrs = lfs.attributes(filec)
|
||||
eq('rwx------', attrs.permissions)
|
||||
eq(0, m.file_close(fp))
|
||||
eq(0, m.file_close(fp, false))
|
||||
end)
|
||||
|
||||
itp('can create a rw------- file with kFileCreateOnly', function()
|
||||
@@ -172,7 +180,7 @@ describe('file_open', function()
|
||||
eq(0, err)
|
||||
local attrs = lfs.attributes(filec)
|
||||
eq('rw-------', attrs.permissions)
|
||||
eq(0, m.file_close(fp))
|
||||
eq(0, m.file_close(fp, false))
|
||||
end)
|
||||
|
||||
itp('fails to open an existing file with kFileCreateOnly', function()
|
||||
@@ -191,35 +199,35 @@ describe('file_open', function()
|
||||
local err, fp = file_open(file1, m.kFileCreate, 384)
|
||||
eq(0, err)
|
||||
eq(true, fp.wr)
|
||||
eq(0, m.file_close(fp))
|
||||
eq(0, m.file_close(fp, false))
|
||||
end)
|
||||
|
||||
itp('can open an existing file read-only with zero', function()
|
||||
local err, fp = file_open(file1, 0, 384)
|
||||
eq(0, err)
|
||||
eq(false, fp.wr)
|
||||
eq(0, m.file_close(fp))
|
||||
eq(0, m.file_close(fp, false))
|
||||
end)
|
||||
|
||||
itp('can open an existing file read-only with kFileReadOnly', function()
|
||||
local err, fp = file_open(file1, m.kFileReadOnly, 384)
|
||||
eq(0, err)
|
||||
eq(false, fp.wr)
|
||||
eq(0, m.file_close(fp))
|
||||
eq(0, m.file_close(fp, false))
|
||||
end)
|
||||
|
||||
itp('can open an existing file read-only with kFileNoSymlink', function()
|
||||
local err, fp = file_open(file1, m.kFileNoSymlink, 384)
|
||||
eq(0, err)
|
||||
eq(false, fp.wr)
|
||||
eq(0, m.file_close(fp))
|
||||
eq(0, m.file_close(fp, false))
|
||||
end)
|
||||
|
||||
itp('can truncate an existing file with kFileTruncate', function()
|
||||
local err, fp = file_open(file1, m.kFileTruncate, 384)
|
||||
eq(0, err)
|
||||
eq(true, fp.wr)
|
||||
eq(0, m.file_close(fp))
|
||||
eq(0, m.file_close(fp, false))
|
||||
local attrs = lfs.attributes(file1)
|
||||
eq(0, attrs.size)
|
||||
end)
|
||||
@@ -228,7 +236,7 @@ describe('file_open', function()
|
||||
local err, fp = file_open(file1, m.kFileWriteOnly, 384)
|
||||
eq(0, err)
|
||||
eq(true, fp.wr)
|
||||
eq(0, m.file_close(fp))
|
||||
eq(0, m.file_close(fp, false))
|
||||
local attrs = lfs.attributes(file1)
|
||||
eq(4096, attrs.size)
|
||||
end)
|
||||
@@ -245,7 +253,7 @@ describe('file_open', function()
|
||||
local err, fp = file_open(linkf, m.kFileTruncate, 384)
|
||||
eq(0, err)
|
||||
eq(true, fp.wr)
|
||||
eq(0, m.file_close(fp))
|
||||
eq(0, m.file_close(fp, false))
|
||||
local attrs = lfs.attributes(file1)
|
||||
eq(0, attrs.size)
|
||||
end)
|
||||
@@ -271,7 +279,7 @@ describe('file_open_new', function()
|
||||
local err, fp = file_open_new(file1, 0, 384)
|
||||
eq(0, err)
|
||||
eq(false, fp.wr)
|
||||
eq(0, m.file_free(fp))
|
||||
eq(0, m.file_free(fp, false))
|
||||
end)
|
||||
|
||||
itp('fails to open an existing file with kFileCreateOnly', function()
|
||||
@@ -281,7 +289,29 @@ describe('file_open_new', function()
|
||||
end)
|
||||
end)
|
||||
|
||||
-- file_close is called above, so it is not tested directly
|
||||
describe('file_close', function()
|
||||
itp('can flush writes to disk also with true argument', function()
|
||||
local err, fp = file_open(filec, m.kFileCreateOnly, 384)
|
||||
eq(0, err)
|
||||
local wsize = file_write(fp, 'test')
|
||||
eq(4, wsize)
|
||||
eq(0, lfs.attributes(filec).size)
|
||||
eq(0, m.file_close(fp, true))
|
||||
eq(wsize, lfs.attributes(filec).size)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('file_free', function()
|
||||
itp('can flush writes to disk also with true argument', function()
|
||||
local err, fp = file_open_new(filec, m.kFileCreateOnly, 384)
|
||||
eq(0, err)
|
||||
local wsize = file_write(fp, 'test')
|
||||
eq(4, wsize)
|
||||
eq(0, lfs.attributes(filec).size)
|
||||
eq(0, m.file_free(fp, true))
|
||||
eq(wsize, lfs.attributes(filec).size)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('file_fsync', function()
|
||||
itp('can flush writes to disk', function()
|
||||
@@ -294,7 +324,22 @@ describe('file_fsync', function()
|
||||
eq(0, lfs.attributes(filec).size)
|
||||
eq(0, file_fsync(fp))
|
||||
eq(wsize, lfs.attributes(filec).size)
|
||||
eq(0, m.file_close(fp))
|
||||
eq(0, m.file_close(fp, false))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('file_flush', function()
|
||||
itp('can flush writes to disk', function()
|
||||
local err, fp = file_open(filec, m.kFileCreateOnly, 384)
|
||||
eq(0, file_flush(fp))
|
||||
eq(0, err)
|
||||
eq(0, lfs.attributes(filec).size)
|
||||
local wsize = file_write(fp, 'test')
|
||||
eq(4, wsize)
|
||||
eq(0, lfs.attributes(filec).size)
|
||||
eq(0, file_flush(fp))
|
||||
eq(wsize, lfs.attributes(filec).size)
|
||||
eq(0, m.file_close(fp, false))
|
||||
end)
|
||||
end)
|
||||
|
||||
@@ -316,7 +361,7 @@ describe('file_read', function()
|
||||
eq({exp_err, exp_s}, {file_read(fp, size)})
|
||||
shift = shift + size
|
||||
end
|
||||
eq(0, m.file_close(fp))
|
||||
eq(0, m.file_close(fp, false))
|
||||
end)
|
||||
|
||||
itp('can read the whole file at once', function()
|
||||
@@ -325,7 +370,7 @@ describe('file_read', function()
|
||||
eq(false, fp.wr)
|
||||
eq({#fcontents, fcontents}, {file_read(fp, #fcontents)})
|
||||
eq({0, ('\0'):rep(#fcontents)}, {file_read(fp, #fcontents)})
|
||||
eq(0, m.file_close(fp))
|
||||
eq(0, m.file_close(fp, false))
|
||||
end)
|
||||
|
||||
itp('can read more then 1024 bytes after reading a small chunk', function()
|
||||
@@ -335,7 +380,7 @@ describe('file_read', function()
|
||||
eq({5, fcontents:sub(1, 5)}, {file_read(fp, 5)})
|
||||
eq({#fcontents - 5, fcontents:sub(6) .. (('\0'):rep(5))},
|
||||
{file_read(fp, #fcontents)})
|
||||
eq(0, m.file_close(fp))
|
||||
eq(0, m.file_close(fp, false))
|
||||
end)
|
||||
|
||||
itp('can read file by 768-byte-chunks', function()
|
||||
@@ -355,7 +400,7 @@ describe('file_read', function()
|
||||
eq({exp_err, exp_s}, {file_read(fp, size)})
|
||||
shift = shift + size
|
||||
end
|
||||
eq(0, m.file_close(fp))
|
||||
eq(0, m.file_close(fp, false))
|
||||
end)
|
||||
end)
|
||||
|
||||
@@ -366,7 +411,7 @@ describe('file_write', function()
|
||||
eq(true, fp.wr)
|
||||
local wr = file_write(fp, fcontents)
|
||||
eq(#fcontents, wr)
|
||||
eq(0, m.file_close(fp))
|
||||
eq(0, m.file_close(fp, false))
|
||||
eq(wr, lfs.attributes(filec).size)
|
||||
eq(fcontents, io.open(filec):read('*a'))
|
||||
end)
|
||||
@@ -383,7 +428,7 @@ describe('file_write', function()
|
||||
eq(wr, #s)
|
||||
shift = shift + size
|
||||
end
|
||||
eq(0, m.file_close(fp))
|
||||
eq(0, m.file_close(fp, false))
|
||||
eq(#fcontents, lfs.attributes(filec).size)
|
||||
eq(fcontents, io.open(filec):read('*a'))
|
||||
end)
|
||||
@@ -400,12 +445,24 @@ describe('file_write', function()
|
||||
eq(wr, #s)
|
||||
shift = shift + size
|
||||
end
|
||||
eq(0, m.file_close(fp))
|
||||
eq(0, m.file_close(fp, false))
|
||||
eq(#fcontents, lfs.attributes(filec).size)
|
||||
eq(fcontents, io.open(filec):read('*a'))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('msgpack_file_write', function()
|
||||
itp('can write the whole file at once', function()
|
||||
local err, fp = file_open(filec, m.kFileCreateOnly, 384)
|
||||
eq(0, err)
|
||||
eq(true, fp.wr)
|
||||
local wr = msgpack_file_write(fp, fcontents)
|
||||
eq(0, wr)
|
||||
eq(0, m.file_close(fp, false))
|
||||
eq(fcontents, io.open(filec):read('*a'))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('file_skip', function()
|
||||
itp('can skip 3 bytes', function()
|
||||
local err, fp = file_open(file1, 0, 384)
|
||||
@@ -415,6 +472,6 @@ describe('file_skip', function()
|
||||
local rd, s = file_read(fp, 3)
|
||||
eq(3, rd)
|
||||
eq(fcontents:sub(4, 6), s)
|
||||
eq(0, m.file_close(fp))
|
||||
eq(0, m.file_close(fp, false))
|
||||
end)
|
||||
end)
|
||||
|
||||
@@ -35,7 +35,6 @@ for i = 0, 255 do
|
||||
end
|
||||
local fcontents = s:rep(16)
|
||||
|
||||
local buffer = ""
|
||||
local directory = nil
|
||||
local absolute_executable = nil
|
||||
local executable_name = nil
|
||||
@@ -65,7 +64,11 @@ local function os_getperm(filename)
|
||||
return tonumber(perm)
|
||||
end
|
||||
|
||||
describe('fs function', function()
|
||||
describe('fs.c', function()
|
||||
local function os_isdir(name)
|
||||
return fs.os_isdir(to_cstr(name))
|
||||
end
|
||||
|
||||
before_each(function()
|
||||
lfs.mkdir('unit-test-directory');
|
||||
|
||||
@@ -91,32 +94,37 @@ describe('fs function', function()
|
||||
end)
|
||||
|
||||
describe('os_dirname', function()
|
||||
local length
|
||||
|
||||
local function os_dirname(buf, len)
|
||||
return fs.os_dirname(buf, len)
|
||||
end
|
||||
|
||||
before_each(function()
|
||||
length = (string.len(lfs.currentdir())) + 1
|
||||
buffer = cstr(length, '')
|
||||
itp('returns OK and writes current directory to the buffer', function()
|
||||
local length = string.len(lfs.currentdir()) + 1
|
||||
local buf = cstr(length, '')
|
||||
eq(OK, fs.os_dirname(buf, length))
|
||||
eq(lfs.currentdir(), ffi.string(buf))
|
||||
end)
|
||||
|
||||
itp('returns OK and writes current directory into the buffer if it is large\n enough', function()
|
||||
eq(OK, (os_dirname(buffer, length)))
|
||||
eq(lfs.currentdir(), (ffi.string(buffer)))
|
||||
end)
|
||||
|
||||
-- What kind of other failing cases are possible?
|
||||
itp('returns FAIL if the buffer is too small', function()
|
||||
local buf = cstr((length - 1), '')
|
||||
eq(FAIL, (os_dirname(buf, (length - 1))))
|
||||
local length = string.len(lfs.currentdir()) + 1
|
||||
local buf = cstr(length - 1, '')
|
||||
eq(FAIL, fs.os_dirname(buf, length - 1))
|
||||
end)
|
||||
end)
|
||||
|
||||
local function os_isdir(name)
|
||||
return fs.os_isdir((to_cstr(name)))
|
||||
end
|
||||
describe('os_chdir', function()
|
||||
itp('fails with path="~"', function()
|
||||
eq(false, os_isdir('~')) -- sanity check: no literal "~" directory.
|
||||
local length = 4096
|
||||
local expected_cwd = cstr(length, '')
|
||||
local cwd = cstr(length, '')
|
||||
eq(OK, fs.os_dirname(expected_cwd, length))
|
||||
|
||||
-- os_chdir returns 0 for success, not OK (1).
|
||||
neq(0, fs.os_chdir('~')) -- fail
|
||||
neq(0, fs.os_chdir('~/')) -- fail
|
||||
|
||||
eq(OK, fs.os_dirname(cwd, length))
|
||||
-- CWD did not change.
|
||||
eq(ffi.string(expected_cwd), ffi.string(cwd))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('os_isdir', function()
|
||||
itp('returns false if an empty string is given', function()
|
||||
@@ -870,6 +878,11 @@ describe('fs function', function()
|
||||
end
|
||||
|
||||
describe('os_fileinfo', function()
|
||||
itp('returns false if path=NULL', function()
|
||||
local file_info = file_info_new()
|
||||
assert.is_false((fs.os_fileinfo(nil, file_info)))
|
||||
end)
|
||||
|
||||
itp('returns false if given a non-existing file', function()
|
||||
local file_info = file_info_new()
|
||||
assert.is_false((fs.os_fileinfo('/non-existent', file_info)))
|
||||
|
||||
@@ -15,7 +15,7 @@ local NULL = ffi.cast('void *', 0)
|
||||
describe('shell functions', function()
|
||||
before_each(function()
|
||||
-- os_system() can't work when the p_sh and p_shcf variables are unset
|
||||
cimported.p_sh = to_cstr('/bin/bash')
|
||||
cimported.p_sh = to_cstr('/bin/sh')
|
||||
cimported.p_shcf = to_cstr('-c')
|
||||
cimported.p_sxq = to_cstr('')
|
||||
cimported.p_sxe = to_cstr('')
|
||||
@@ -53,14 +53,14 @@ describe('shell functions', function()
|
||||
|
||||
describe('os_system', function()
|
||||
itp('can echo some output (shell builtin)', function()
|
||||
local cmd, text = 'echo -n', 'some text'
|
||||
local cmd, text = 'printf "%s "', 'some text '
|
||||
local status, output = os_system(cmd .. ' ' .. text)
|
||||
eq(text, output)
|
||||
eq(0, status)
|
||||
end)
|
||||
|
||||
itp('can deal with empty output', function()
|
||||
local cmd = 'echo -n'
|
||||
local cmd = 'printf ""'
|
||||
local status, output = os_system(cmd)
|
||||
eq('', output)
|
||||
eq(0, status)
|
||||
@@ -81,19 +81,19 @@ describe('shell functions', function()
|
||||
|
||||
describe('shell_build_argv', function()
|
||||
itp('works with NULL arguments', function()
|
||||
eq({'/bin/bash'}, shell_build_argv(nil, nil))
|
||||
eq({'/bin/sh'}, shell_build_argv(nil, nil))
|
||||
end)
|
||||
|
||||
itp('works with cmd', function()
|
||||
eq({'/bin/bash', '-c', 'abc def'}, shell_build_argv('abc def', nil))
|
||||
eq({'/bin/sh', '-c', 'abc def'}, shell_build_argv('abc def', nil))
|
||||
end)
|
||||
|
||||
itp('works with extra_args', function()
|
||||
eq({'/bin/bash', 'ghi jkl'}, shell_build_argv(nil, 'ghi jkl'))
|
||||
eq({'/bin/sh', 'ghi jkl'}, shell_build_argv(nil, 'ghi jkl'))
|
||||
end)
|
||||
|
||||
itp('works with cmd and extra_args', function()
|
||||
eq({'/bin/bash', 'ghi jkl', '-c', 'abc def'}, shell_build_argv('abc def', 'ghi jkl'))
|
||||
eq({'/bin/sh', 'ghi jkl', '-c', 'abc def'}, shell_build_argv('abc def', 'ghi jkl'))
|
||||
end)
|
||||
|
||||
itp('splits and unquotes &shell and &shellcmdflag', function()
|
||||
@@ -112,7 +112,7 @@ describe('shell functions', function()
|
||||
|
||||
local argv = ffi.cast('char**',
|
||||
cimported.shell_build_argv(to_cstr('echo &|<>()@^'), nil))
|
||||
eq(ffi.string(argv[0]), '/bin/bash')
|
||||
eq(ffi.string(argv[0]), '/bin/sh')
|
||||
eq(ffi.string(argv[1]), '-c')
|
||||
eq(ffi.string(argv[2]), '(echo ^&^|^<^>^(^)^@^^)')
|
||||
eq(nil, argv[3])
|
||||
@@ -124,7 +124,7 @@ describe('shell functions', function()
|
||||
|
||||
local argv = ffi.cast('char**', cimported.shell_build_argv(
|
||||
to_cstr('echo -n some text'), nil))
|
||||
eq(ffi.string(argv[0]), '/bin/bash')
|
||||
eq(ffi.string(argv[0]), '/bin/sh')
|
||||
eq(ffi.string(argv[1]), '-c')
|
||||
eq(ffi.string(argv[2]), '"(echo -n some text)"')
|
||||
eq(nil, argv[3])
|
||||
@@ -136,7 +136,7 @@ describe('shell functions', function()
|
||||
|
||||
local argv = ffi.cast('char**', cimported.shell_build_argv(
|
||||
to_cstr('echo -n some text'), nil))
|
||||
eq(ffi.string(argv[0]), '/bin/bash')
|
||||
eq(ffi.string(argv[0]), '/bin/sh')
|
||||
eq(ffi.string(argv[1]), '-c')
|
||||
eq(ffi.string(argv[2]), '"echo -n some text"')
|
||||
eq(nil, argv[3])
|
||||
@@ -145,7 +145,7 @@ describe('shell functions', function()
|
||||
itp('with empty shellxquote/shellxescape', function()
|
||||
local argv = ffi.cast('char**', cimported.shell_build_argv(
|
||||
to_cstr('echo -n some text'), nil))
|
||||
eq(ffi.string(argv[0]), '/bin/bash')
|
||||
eq(ffi.string(argv[0]), '/bin/sh')
|
||||
eq(ffi.string(argv[1]), '-c')
|
||||
eq(ffi.string(argv[2]), 'echo -n some text')
|
||||
eq(nil, argv[3])
|
||||
|
||||
@@ -13,12 +13,12 @@ local OK = helpers.OK
|
||||
local FAIL = helpers.FAIL
|
||||
|
||||
cimport('string.h')
|
||||
local path = cimport('./src/nvim/path.h')
|
||||
local cimp = cimport('./src/nvim/os/os.h', './src/nvim/path.h')
|
||||
|
||||
local length = 0
|
||||
local buffer = nil
|
||||
|
||||
describe('path function', function()
|
||||
describe('path.c', function()
|
||||
describe('path_full_dir_name', function()
|
||||
setup(function()
|
||||
lfs.mkdir('unit-test-directory')
|
||||
@@ -30,7 +30,7 @@ describe('path function', function()
|
||||
|
||||
local function path_full_dir_name(directory, buf, len)
|
||||
directory = to_cstr(directory)
|
||||
return path.path_full_dir_name(directory, buf, len)
|
||||
return cimp.path_full_dir_name(directory, buf, len)
|
||||
end
|
||||
|
||||
before_each(function()
|
||||
@@ -69,7 +69,7 @@ describe('path function', function()
|
||||
local function path_full_compare(s1, s2, cn)
|
||||
s1 = to_cstr(s1)
|
||||
s2 = to_cstr(s2)
|
||||
return path.path_full_compare(s1, s2, cn or 0)
|
||||
return cimp.path_full_compare(s1, s2, cn or 0)
|
||||
end
|
||||
|
||||
local f1 = 'f1.o'
|
||||
@@ -86,31 +86,31 @@ describe('path function', function()
|
||||
end)
|
||||
|
||||
itp('returns kEqualFiles when passed the same file', function()
|
||||
eq(path.kEqualFiles, (path_full_compare(f1, f1)))
|
||||
eq(cimp.kEqualFiles, (path_full_compare(f1, f1)))
|
||||
end)
|
||||
|
||||
itp('returns kEqualFileNames when files that dont exist and have same name', function()
|
||||
eq(path.kEqualFileNames, (path_full_compare('null.txt', 'null.txt', true)))
|
||||
eq(cimp.kEqualFileNames, (path_full_compare('null.txt', 'null.txt', true)))
|
||||
end)
|
||||
|
||||
itp('returns kBothFilesMissing when files that dont exist', function()
|
||||
eq(path.kBothFilesMissing, (path_full_compare('null.txt', 'null.txt')))
|
||||
eq(cimp.kBothFilesMissing, (path_full_compare('null.txt', 'null.txt')))
|
||||
end)
|
||||
|
||||
itp('returns kDifferentFiles when passed different files', function()
|
||||
eq(path.kDifferentFiles, (path_full_compare(f1, f2)))
|
||||
eq(path.kDifferentFiles, (path_full_compare(f2, f1)))
|
||||
eq(cimp.kDifferentFiles, (path_full_compare(f1, f2)))
|
||||
eq(cimp.kDifferentFiles, (path_full_compare(f2, f1)))
|
||||
end)
|
||||
|
||||
itp('returns kOneFileMissing if only one does not exist', function()
|
||||
eq(path.kOneFileMissing, (path_full_compare(f1, 'null.txt')))
|
||||
eq(path.kOneFileMissing, (path_full_compare('null.txt', f1)))
|
||||
eq(cimp.kOneFileMissing, (path_full_compare(f1, 'null.txt')))
|
||||
eq(cimp.kOneFileMissing, (path_full_compare('null.txt', f1)))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('path_tail', function()
|
||||
local function path_tail(file)
|
||||
local res = path.path_tail((to_cstr(file)))
|
||||
local res = cimp.path_tail((to_cstr(file)))
|
||||
neq(NULL, res)
|
||||
return ffi.string(res)
|
||||
end
|
||||
@@ -126,7 +126,7 @@ describe('path function', function()
|
||||
|
||||
describe('path_tail_with_sep', function()
|
||||
local function path_tail_with_sep(file)
|
||||
local res = path.path_tail_with_sep((to_cstr(file)))
|
||||
local res = cimp.path_tail_with_sep((to_cstr(file)))
|
||||
neq(NULL, res)
|
||||
return ffi.string(res)
|
||||
end
|
||||
@@ -159,11 +159,11 @@ describe('path function', function()
|
||||
-- strcmp.
|
||||
local function invocation_path_tail(invk)
|
||||
local plen = ffi.new('size_t[?]', 1)
|
||||
local ptail = path.invocation_path_tail((to_cstr(invk)), plen)
|
||||
local ptail = cimp.invocation_path_tail((to_cstr(invk)), plen)
|
||||
neq(NULL, ptail)
|
||||
|
||||
-- it does not change the output if len==NULL
|
||||
local tail2 = path.invocation_path_tail((to_cstr(invk)), NULL)
|
||||
local tail2 = cimp.invocation_path_tail((to_cstr(invk)), NULL)
|
||||
neq(NULL, tail2)
|
||||
eq((ffi.string(ptail)), (ffi.string(tail2)))
|
||||
return ptail, plen[0]
|
||||
@@ -204,7 +204,7 @@ describe('path function', function()
|
||||
end)
|
||||
|
||||
itp('is equivalent to path_tail when args do not contain a path separator', function()
|
||||
local ptail = path.path_tail(to_cstr("a/b/c x y z"))
|
||||
local ptail = cimp.path_tail(to_cstr("a/b/c x y z"))
|
||||
neq(NULL, ptail)
|
||||
local tail = ffi.string(ptail)
|
||||
local invk, _ = invocation_path_tail("a/b/c x y z")
|
||||
@@ -212,7 +212,7 @@ describe('path function', function()
|
||||
end)
|
||||
|
||||
itp('is not equivalent to path_tail when args contain a path separator', function()
|
||||
local ptail = path.path_tail(to_cstr("a/b/c x y/z"))
|
||||
local ptail = cimp.path_tail(to_cstr("a/b/c x y/z"))
|
||||
neq(NULL, ptail)
|
||||
local invk, _ = invocation_path_tail("a/b/c x y/z")
|
||||
neq((ffi.string(ptail)), (ffi.string(invk)))
|
||||
@@ -221,7 +221,7 @@ describe('path function', function()
|
||||
|
||||
describe('path_next_component', function()
|
||||
local function path_next_component(file)
|
||||
local res = path.path_next_component((to_cstr(file)))
|
||||
local res = cimp.path_next_component((to_cstr(file)))
|
||||
neq(NULL, res)
|
||||
return ffi.string(res)
|
||||
end
|
||||
@@ -238,25 +238,25 @@ describe('path function', function()
|
||||
describe('path_shorten_fname', function()
|
||||
itp('returns NULL if `full_path` is NULL', function()
|
||||
local dir = to_cstr('some/directory/file.txt')
|
||||
eq(NULL, (path.path_shorten_fname(NULL, dir)))
|
||||
eq(NULL, (cimp.path_shorten_fname(NULL, dir)))
|
||||
end)
|
||||
|
||||
itp('returns NULL if the path and dir does not match', function()
|
||||
local dir = to_cstr('not/the/same')
|
||||
local full = to_cstr('as/this.txt')
|
||||
eq(NULL, (path.path_shorten_fname(full, dir)))
|
||||
eq(NULL, (cimp.path_shorten_fname(full, dir)))
|
||||
end)
|
||||
|
||||
itp('returns NULL if the path is not separated properly', function()
|
||||
local dir = to_cstr('some/very/long/')
|
||||
local full = to_cstr('some/very/long/directory/file.txt')
|
||||
eq(NULL, (path.path_shorten_fname(full, dir)))
|
||||
eq(NULL, (cimp.path_shorten_fname(full, dir)))
|
||||
end)
|
||||
|
||||
itp('shortens the filename if `dir_name` is the start of `full_path`', function()
|
||||
local full = to_cstr('some/very/long/directory/file.txt')
|
||||
local dir = to_cstr('some/very/long')
|
||||
eq('directory/file.txt', (ffi.string(path.path_shorten_fname(full, dir))))
|
||||
eq('directory/file.txt', (ffi.string(cimp.path_shorten_fname(full, dir))))
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
@@ -277,23 +277,76 @@ describe('path_shorten_fname_if_possible', function()
|
||||
itp('returns shortened path if possible', function()
|
||||
lfs.chdir('ut_directory')
|
||||
local full = to_cstr(lfs.currentdir() .. '/subdir/file.txt')
|
||||
eq('subdir/file.txt', (ffi.string(path.path_shorten_fname_if_possible(full))))
|
||||
eq('subdir/file.txt', (ffi.string(cimp.path_shorten_fname_if_possible(full))))
|
||||
end)
|
||||
|
||||
itp('returns `full_path` if a shorter version is not possible', function()
|
||||
local old = lfs.currentdir()
|
||||
lfs.chdir('ut_directory')
|
||||
local full = old .. '/subdir/file.txt'
|
||||
eq(full, (ffi.string(path.path_shorten_fname_if_possible(to_cstr(full)))))
|
||||
eq(full, (ffi.string(cimp.path_shorten_fname_if_possible(to_cstr(full)))))
|
||||
end)
|
||||
|
||||
itp('returns NULL if `full_path` is NULL', function()
|
||||
eq(NULL, (path.path_shorten_fname_if_possible(NULL)))
|
||||
eq(NULL, (cimp.path_shorten_fname_if_possible(NULL)))
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('more path function', function()
|
||||
describe('path.c path_guess_exepath', function()
|
||||
local cwd = lfs.currentdir()
|
||||
|
||||
for _,name in ipairs({'./nvim', '.nvim', 'foo/nvim'}) do
|
||||
itp('"'..name..'" returns name catenated with CWD', function()
|
||||
local bufsize = 255
|
||||
local buf = cstr(bufsize, '')
|
||||
cimp.path_guess_exepath(name, buf, bufsize)
|
||||
eq(cwd..'/'..name, ffi.string(buf))
|
||||
end)
|
||||
end
|
||||
|
||||
itp('absolute path returns the name unmodified', function()
|
||||
local name = '/foo/bar/baz'
|
||||
local bufsize = 255
|
||||
local buf = cstr(bufsize, '')
|
||||
cimp.path_guess_exepath(name, buf, bufsize)
|
||||
eq(name, ffi.string(buf))
|
||||
end)
|
||||
|
||||
itp('returns the name unmodified if not found in $PATH', function()
|
||||
local name = '23u0293_not_in_path'
|
||||
local bufsize = 255
|
||||
local buf = cstr(bufsize, '')
|
||||
cimp.path_guess_exepath(name, buf, bufsize)
|
||||
eq(name, ffi.string(buf))
|
||||
end)
|
||||
|
||||
itp('does not crash if $PATH item exceeds MAXPATHL', function()
|
||||
local orig_path_env = os.getenv('PATH')
|
||||
local name = 'cat' -- Some executable in $PATH.
|
||||
local bufsize = 255
|
||||
local buf = cstr(bufsize, '')
|
||||
local insane_path = orig_path_env..':'..(("x/"):rep(4097))
|
||||
|
||||
cimp.os_setenv('PATH', insane_path, true)
|
||||
cimp.path_guess_exepath(name, buf, bufsize)
|
||||
eq('bin/' .. name, ffi.string(buf):sub(-#('bin/' .. name), -1))
|
||||
|
||||
-- Restore $PATH.
|
||||
cimp.os_setenv('PATH', orig_path_env, true)
|
||||
end)
|
||||
|
||||
itp('returns full path found in $PATH', function()
|
||||
local name = 'cat' -- Some executable in $PATH.
|
||||
local bufsize = 255
|
||||
local buf = cstr(bufsize, '')
|
||||
cimp.path_guess_exepath(name, buf, bufsize)
|
||||
-- Usually "/bin/cat" on unix, "/path/to/nvim/cat" on Windows.
|
||||
eq('bin/' .. name, ffi.string(buf):sub(-#('bin/' .. name), -1))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('path.c', function()
|
||||
setup(function()
|
||||
lfs.mkdir('unit-test-directory');
|
||||
io.open('unit-test-directory/test.file', 'w').close()
|
||||
@@ -313,126 +366,163 @@ describe('more path function', function()
|
||||
end)
|
||||
|
||||
describe('vim_FullName', function()
|
||||
local function vim_FullName(filename, buf, len, force)
|
||||
filename = to_cstr(filename)
|
||||
return path.vim_FullName(filename, buf, len, force)
|
||||
local function vim_FullName(filename, buflen, do_expand)
|
||||
local buf = cstr(buflen, '')
|
||||
local result = cimp.vim_FullName(to_cstr(filename), buf, buflen, do_expand)
|
||||
return buf, result
|
||||
end
|
||||
|
||||
before_each(function()
|
||||
-- Create empty string buffer which will contain the resulting path.
|
||||
length = (string.len(lfs.currentdir())) + 33
|
||||
buffer = cstr(length, '')
|
||||
end)
|
||||
local function get_buf_len(s, t)
|
||||
return math.max(string.len(s), string.len(t)) + 1
|
||||
end
|
||||
|
||||
itp('fails if given filename is NULL', function()
|
||||
local force_expansion = 1
|
||||
local result = path.vim_FullName(NULL, buffer, length, force_expansion)
|
||||
local do_expand = 1
|
||||
local buflen = 10
|
||||
local buf = cstr(buflen, '')
|
||||
local result = cimp.vim_FullName(NULL, buf, buflen, do_expand)
|
||||
eq(FAIL, result)
|
||||
end)
|
||||
|
||||
itp('fails safely if given length is wrong #5737', function()
|
||||
local force_expansion = 1
|
||||
local filename = 'foo/bar/bazzzzzzz/buz/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/a'
|
||||
local too_short_len = 8
|
||||
local buf = cstr(too_short_len, '')
|
||||
local result = path.vim_FullName(filename, buf, too_short_len, force_expansion)
|
||||
local do_expand = 1
|
||||
local result = cimp.vim_FullName(filename, buf, too_short_len, do_expand)
|
||||
local expected = string.sub(filename, 1, (too_short_len - 1))
|
||||
eq(expected, (ffi.string(buf)))
|
||||
eq(expected, ffi.string(buf))
|
||||
eq(FAIL, result)
|
||||
end)
|
||||
|
||||
itp('uses the filename if the filename is a URL', function()
|
||||
local force_expansion = 1
|
||||
local filename = 'http://www.neovim.org'
|
||||
local result = vim_FullName(filename, buffer, length, force_expansion)
|
||||
eq(filename, (ffi.string(buffer)))
|
||||
local buflen = string.len(filename) + 1
|
||||
local do_expand = 1
|
||||
local buf, result = vim_FullName(filename, buflen, do_expand)
|
||||
eq(filename, ffi.string(buf))
|
||||
eq(OK, result)
|
||||
end)
|
||||
|
||||
itp('fails and uses filename if given filename contains non-existing directory', function()
|
||||
local force_expansion = 1
|
||||
local filename = 'non_existing_dir/test.file'
|
||||
local result = vim_FullName(filename, buffer, length, force_expansion)
|
||||
eq(filename, (ffi.string(buffer)))
|
||||
local buflen = string.len(filename) + 1
|
||||
local do_expand = 1
|
||||
local buf, result = vim_FullName(filename, buflen, do_expand)
|
||||
eq(filename, ffi.string(buf))
|
||||
eq(FAIL, result)
|
||||
end)
|
||||
|
||||
itp('concatenates given filename if it does not contain a slash', function()
|
||||
local force_expansion = 1
|
||||
local result = vim_FullName('test.file', buffer, length, force_expansion)
|
||||
itp('concatenates filename if it does not contain a slash', function()
|
||||
local expected = lfs.currentdir() .. '/test.file'
|
||||
eq(expected, (ffi.string(buffer)))
|
||||
local filename = 'test.file'
|
||||
local buflen = get_buf_len(expected, filename)
|
||||
local do_expand = 1
|
||||
local buf, result = vim_FullName(filename, buflen, do_expand)
|
||||
eq(expected, ffi.string(buf))
|
||||
eq(OK, result)
|
||||
end)
|
||||
|
||||
itp('concatenates given filename if it is a directory but does not contain a\n slash', function()
|
||||
local force_expansion = 1
|
||||
local result = vim_FullName('..', buffer, length, force_expansion)
|
||||
itp('concatenates directory name if it does not contain a slash', function()
|
||||
local expected = lfs.currentdir() .. '/..'
|
||||
eq(expected, (ffi.string(buffer)))
|
||||
local filename = '..'
|
||||
local buflen = get_buf_len(expected, filename)
|
||||
local do_expand = 1
|
||||
local buf, result = vim_FullName(filename, buflen, do_expand)
|
||||
eq(expected, ffi.string(buf))
|
||||
eq(OK, result)
|
||||
end)
|
||||
|
||||
-- Is it possible for every developer to enter '..' directory while running
|
||||
-- the unit tests? Which other directory would be better?
|
||||
itp('enters given directory (instead of just concatenating the strings) if possible and if path contains a slash', function()
|
||||
local force_expansion = 1
|
||||
local result = vim_FullName('../test.file', buffer, length, force_expansion)
|
||||
local old_dir = lfs.currentdir()
|
||||
lfs.chdir('..')
|
||||
local expected = lfs.currentdir() .. '/test.file'
|
||||
lfs.chdir(old_dir)
|
||||
eq(expected, (ffi.string(buffer)))
|
||||
local filename = '../test.file'
|
||||
local buflen = get_buf_len(expected, filename)
|
||||
local do_expand = 1
|
||||
local buf, result = vim_FullName(filename, buflen, do_expand)
|
||||
eq(expected, ffi.string(buf))
|
||||
eq(OK, result)
|
||||
end)
|
||||
|
||||
itp('just copies the path if it is already absolute and force=0', function()
|
||||
local force_expansion = 0
|
||||
local absolute_path = '/absolute/path'
|
||||
local result = vim_FullName(absolute_path, buffer, length, force_expansion)
|
||||
eq(absolute_path, (ffi.string(buffer)))
|
||||
local buflen = string.len(absolute_path) + 1
|
||||
local do_expand = 0
|
||||
local buf, result = vim_FullName(absolute_path, buflen, do_expand)
|
||||
eq(absolute_path, ffi.string(buf))
|
||||
eq(OK, result)
|
||||
end)
|
||||
|
||||
itp('fails and uses filename when the path is relative to HOME', function()
|
||||
local force_expansion = 1
|
||||
eq(false, cimp.os_isdir('~')) -- sanity check: no literal "~" directory.
|
||||
local absolute_path = '~/home.file'
|
||||
local result = vim_FullName(absolute_path, buffer, length, force_expansion)
|
||||
eq(absolute_path, (ffi.string(buffer)))
|
||||
local buflen = string.len(absolute_path) + 1
|
||||
local do_expand = 1
|
||||
local buf, result = vim_FullName(absolute_path, buflen, do_expand)
|
||||
eq(absolute_path, ffi.string(buf))
|
||||
eq(FAIL, result)
|
||||
end)
|
||||
|
||||
itp('works with some "normal" relative path with directories', function()
|
||||
local force_expansion = 1
|
||||
local result = vim_FullName('unit-test-directory/test.file', buffer, length, force_expansion)
|
||||
local expected = lfs.currentdir() .. '/unit-test-directory/test.file'
|
||||
local filename = 'unit-test-directory/test.file'
|
||||
local buflen = get_buf_len(expected, filename)
|
||||
local do_expand = 1
|
||||
local buf, result = vim_FullName(filename, buflen, do_expand)
|
||||
eq(expected, ffi.string(buf))
|
||||
eq(OK, result)
|
||||
eq(lfs.currentdir() .. '/unit-test-directory/test.file', (ffi.string(buffer)))
|
||||
end)
|
||||
|
||||
itp('does not modify the given filename', function()
|
||||
local force_expansion = 1
|
||||
local expected = lfs.currentdir() .. '/unit-test-directory/test.file'
|
||||
local filename = to_cstr('unit-test-directory/test.file')
|
||||
-- Don't use the wrapper here but pass a cstring directly to the c
|
||||
-- function.
|
||||
local result = path.vim_FullName(filename, buffer, length, force_expansion)
|
||||
eq(lfs.currentdir() .. '/unit-test-directory/test.file', (ffi.string(buffer)))
|
||||
eq('unit-test-directory/test.file', (ffi.string(filename)))
|
||||
local buflen = string.len(expected) + 1
|
||||
local buf = cstr(buflen, '')
|
||||
local do_expand = 1
|
||||
-- Don't use the wrapper but pass a cstring directly to the c function.
|
||||
eq('unit-test-directory/test.file', ffi.string(filename))
|
||||
local result = cimp.vim_FullName(filename, buf, buflen, do_expand)
|
||||
eq(expected, ffi.string(buf))
|
||||
eq(OK, result)
|
||||
end)
|
||||
|
||||
itp('works with directories that have one path component', function()
|
||||
local force_expansion = 1
|
||||
local filename = to_cstr('/tmp')
|
||||
local result = path.vim_FullName(filename, buffer, length, force_expansion)
|
||||
eq('/tmp', ffi.string(buffer))
|
||||
local filename = '/tmp'
|
||||
local expected = filename
|
||||
local buflen = get_buf_len(expected, filename)
|
||||
local do_expand = 1
|
||||
local buf, result = vim_FullName(filename, buflen, do_expand)
|
||||
eq('/tmp', ffi.string(buf))
|
||||
eq(OK, result)
|
||||
end)
|
||||
|
||||
itp('expands "./" to the current directory #7117', function()
|
||||
local expected = lfs.currentdir() .. '/unit-test-directory/test.file'
|
||||
local filename = './unit-test-directory/test.file'
|
||||
local buflen = get_buf_len(expected, filename)
|
||||
local do_expand = 1
|
||||
local buf, result = vim_FullName(filename, buflen, do_expand)
|
||||
eq(OK, result)
|
||||
eq(expected, ffi.string(buf))
|
||||
end)
|
||||
|
||||
itp('collapses "foo/../foo" to "foo" #7117', function()
|
||||
local expected = lfs.currentdir() .. '/unit-test-directory/test.file'
|
||||
local filename = 'unit-test-directory/../unit-test-directory/test.file'
|
||||
local buflen = get_buf_len(expected, filename)
|
||||
local do_expand = 1
|
||||
local buf, result = vim_FullName(filename, buflen, do_expand)
|
||||
eq(OK, result)
|
||||
eq(expected, ffi.string(buf))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('path_fix_case', function()
|
||||
local function fix_case(file)
|
||||
local c_file = to_cstr(file)
|
||||
path.path_fix_case(c_file)
|
||||
cimp.path_fix_case(c_file)
|
||||
return ffi.string(c_file)
|
||||
end
|
||||
|
||||
@@ -456,41 +546,41 @@ describe('more path function', function()
|
||||
itp('joins given paths with a slash', function()
|
||||
local path1 = cstr(100, 'path1')
|
||||
local to_append = to_cstr('path2')
|
||||
eq(OK, (path.append_path(path1, to_append, 100)))
|
||||
eq(OK, (cimp.append_path(path1, to_append, 100)))
|
||||
eq("path1/path2", (ffi.string(path1)))
|
||||
end)
|
||||
|
||||
itp('joins given paths without adding an unnecessary slash', function()
|
||||
local path1 = cstr(100, 'path1/')
|
||||
local to_append = to_cstr('path2')
|
||||
eq(OK, path.append_path(path1, to_append, 100))
|
||||
eq(OK, cimp.append_path(path1, to_append, 100))
|
||||
eq("path1/path2", (ffi.string(path1)))
|
||||
end)
|
||||
|
||||
itp('fails and uses filename if there is not enough space left for to_append', function()
|
||||
local path1 = cstr(11, 'path1/')
|
||||
local to_append = to_cstr('path2')
|
||||
eq(FAIL, (path.append_path(path1, to_append, 11)))
|
||||
eq(FAIL, (cimp.append_path(path1, to_append, 11)))
|
||||
end)
|
||||
|
||||
itp('does not append a slash if to_append is empty', function()
|
||||
local path1 = cstr(6, 'path1')
|
||||
local to_append = to_cstr('')
|
||||
eq(OK, (path.append_path(path1, to_append, 6)))
|
||||
eq(OK, (cimp.append_path(path1, to_append, 6)))
|
||||
eq('path1', (ffi.string(path1)))
|
||||
end)
|
||||
|
||||
itp('does not append unnecessary dots', function()
|
||||
local path1 = cstr(6, 'path1')
|
||||
local to_append = to_cstr('.')
|
||||
eq(OK, (path.append_path(path1, to_append, 6)))
|
||||
eq(OK, (cimp.append_path(path1, to_append, 6)))
|
||||
eq('path1', (ffi.string(path1)))
|
||||
end)
|
||||
|
||||
itp('copies to_append to path, if path is empty', function()
|
||||
local path1 = cstr(7, '')
|
||||
local to_append = to_cstr('/path2')
|
||||
eq(OK, (path.append_path(path1, to_append, 7)))
|
||||
eq(OK, (cimp.append_path(path1, to_append, 7)))
|
||||
eq('/path2', (ffi.string(path1)))
|
||||
end)
|
||||
end)
|
||||
@@ -498,7 +588,7 @@ describe('more path function', function()
|
||||
describe('path_is_absolute_path', function()
|
||||
local function path_is_absolute_path(filename)
|
||||
filename = to_cstr(filename)
|
||||
return path.path_is_absolute_path(filename)
|
||||
return cimp.path_is_absolute_path(filename)
|
||||
end
|
||||
|
||||
itp('returns true if filename starts with a slash', function()
|
||||
|
||||
@@ -99,3 +99,42 @@ describe('vim_strnsave_unquoted()', function()
|
||||
eq('/Program\\nFiles/sh', vim_strnsave_unquoted('/Program"\\n"Files/sh'))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('vim_strchr()', function()
|
||||
local vim_strchr = function(s, c)
|
||||
local str = to_cstr(s)
|
||||
local res = strings.vim_strchr(str, c)
|
||||
if res == nil then
|
||||
return nil
|
||||
else
|
||||
return res - str
|
||||
end
|
||||
end
|
||||
itp('handles NUL and <0 correctly', function()
|
||||
eq(nil, vim_strchr('abc', 0))
|
||||
eq(nil, vim_strchr('abc', -1))
|
||||
end)
|
||||
itp('works', function()
|
||||
eq(0, vim_strchr('abc', ('a'):byte()))
|
||||
eq(1, vim_strchr('abc', ('b'):byte()))
|
||||
eq(2, vim_strchr('abc', ('c'):byte()))
|
||||
eq(0, vim_strchr('a«b»c', ('a'):byte()))
|
||||
eq(3, vim_strchr('a«b»c', ('b'):byte()))
|
||||
eq(6, vim_strchr('a«b»c', ('c'):byte()))
|
||||
|
||||
eq(nil, vim_strchr('«»', ('«'):byte()))
|
||||
-- 0xAB == 171 == '«'
|
||||
eq(nil, vim_strchr('\171', 0xAB))
|
||||
eq(0, vim_strchr('«»', 0xAB))
|
||||
eq(3, vim_strchr('„«»“', 0xAB))
|
||||
|
||||
eq(7, vim_strchr('„«»“', 0x201C))
|
||||
eq(nil, vim_strchr('„«»“', 0x201D))
|
||||
eq(0, vim_strchr('„«»“', 0x201E))
|
||||
|
||||
eq(0, vim_strchr('\244\143\188\128', 0x10FF00))
|
||||
eq(2, vim_strchr('«\244\143\188\128»', 0x10FF00))
|
||||
-- |0xDBFF |0xDF00 - surrogate pair for 0x10FF00
|
||||
eq(nil, vim_strchr('«\237\175\191\237\188\128»', 0x10FF00))
|
||||
end)
|
||||
end)
|
||||
|
||||
19
test/unit/testtest_spec.lua
Normal file
19
test/unit/testtest_spec.lua
Normal file
@@ -0,0 +1,19 @@
|
||||
local helpers = require('test.unit.helpers')(after_each)
|
||||
local assert = require('luassert')
|
||||
|
||||
local itp = helpers.gen_itp(it)
|
||||
|
||||
local sc = helpers.sc
|
||||
|
||||
-- All of the below tests must fail. Check how exactly they fail.
|
||||
if os.getenv('NVIM_TEST_RUN_TESTTEST') ~= '1' then
|
||||
return
|
||||
end
|
||||
describe('test code', function()
|
||||
itp('does not hang when working with lengthy errors', function()
|
||||
assert.just_fail(('x'):rep(65536))
|
||||
end)
|
||||
itp('shows trace after exiting abnormally', function()
|
||||
sc.exit(0)
|
||||
end)
|
||||
end)
|
||||
Reference in New Issue
Block a user