aboutsummaryrefslogtreecommitdiff
path: root/test/unit
diff options
context:
space:
mode:
Diffstat (limited to 'test/unit')
-rw-r--r--test/unit/eval/decode_spec.lua142
-rw-r--r--test/unit/eval/encode_spec.lua100
-rw-r--r--test/unit/eval/helpers.lua72
-rw-r--r--test/unit/eval/tricks_spec.lua43
-rw-r--r--test/unit/formatc.lua2
-rw-r--r--test/unit/helpers.lua12
-rw-r--r--test/unit/os/shell_spec.lua31
-rw-r--r--test/unit/tempfile_spec.lua2
8 files changed, 384 insertions, 20 deletions
diff --git a/test/unit/eval/decode_spec.lua b/test/unit/eval/decode_spec.lua
new file mode 100644
index 0000000000..d94d809c14
--- /dev/null
+++ b/test/unit/eval/decode_spec.lua
@@ -0,0 +1,142 @@
+local helpers = require('test.unit.helpers')
+
+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',
+ './src/nvim/globals.h', './src/nvim/memory.h',
+ './src/nvim/message.h')
+
+describe('json_decode_string()', function()
+ local saved_p_enc = nil
+
+ before_each(function()
+ saved_p_enc = decode.p_enc
+ end)
+
+ after_each(function()
+ decode.emsg_silent = 0
+ decode.p_enc = saved_p_enc
+ while decode.delete_first_msg() == 1 do
+ -- Delete all messages
+ end
+ end)
+
+ local char = function(c)
+ return ffi.gc(decode.xmemdup(c, 1), decode.xfree)
+ end
+
+ it('does not overflow when running with `n…`, `t…`, `f…`', function()
+ local rettv = ffi.new('typval_T', {v_type=decode.VAR_UNKNOWN})
+ decode.emsg_silent = 1
+ -- This will not crash, but if `len` argument will be ignored it will parse
+ -- `null` as `null` and if not it will parse `null` as `n`.
+ eq(0, decode.json_decode_string('null', 1, rettv))
+ eq(decode.VAR_UNKNOWN, rettv.v_type)
+ eq(0, decode.json_decode_string('true', 1, rettv))
+ eq(decode.VAR_UNKNOWN, rettv.v_type)
+ eq(0, decode.json_decode_string('false', 1, rettv))
+ eq(decode.VAR_UNKNOWN, rettv.v_type)
+ eq(0, decode.json_decode_string('null', 2, rettv))
+ eq(decode.VAR_UNKNOWN, rettv.v_type)
+ eq(0, decode.json_decode_string('true', 2, rettv))
+ eq(decode.VAR_UNKNOWN, rettv.v_type)
+ eq(0, decode.json_decode_string('false', 2, rettv))
+ eq(decode.VAR_UNKNOWN, rettv.v_type)
+ eq(0, decode.json_decode_string('null', 3, rettv))
+ eq(decode.VAR_UNKNOWN, rettv.v_type)
+ eq(0, decode.json_decode_string('true', 3, rettv))
+ eq(decode.VAR_UNKNOWN, rettv.v_type)
+ eq(0, decode.json_decode_string('false', 3, rettv))
+ eq(decode.VAR_UNKNOWN, rettv.v_type)
+ eq(0, decode.json_decode_string('false', 4, rettv))
+ eq(decode.VAR_UNKNOWN, rettv.v_type)
+ end)
+
+ it('does not overflow and crash when running with `n`, `t`, `f`', function()
+ local rettv = ffi.new('typval_T', {v_type=decode.VAR_UNKNOWN})
+ decode.emsg_silent = 1
+ eq(0, decode.json_decode_string(char('n'), 1, rettv))
+ eq(decode.VAR_UNKNOWN, rettv.v_type)
+ eq(0, decode.json_decode_string(char('t'), 1, rettv))
+ eq(decode.VAR_UNKNOWN, rettv.v_type)
+ eq(0, decode.json_decode_string(char('f'), 1, rettv))
+ eq(decode.VAR_UNKNOWN, rettv.v_type)
+ end)
+
+ it('does not overflow when running with `"…`', function()
+ local rettv = ffi.new('typval_T', {v_type=decode.VAR_UNKNOWN})
+ decode.emsg_silent = 1
+ eq(0, decode.json_decode_string('"t"', 2, rettv))
+ eq(decode.VAR_UNKNOWN, rettv.v_type)
+ eq(0, decode.json_decode_string('""', 1, rettv))
+ eq(decode.VAR_UNKNOWN, rettv.v_type)
+ end)
+
+ local check_failure = function(s, len, msg)
+ local rettv = ffi.new('typval_T', {v_type=decode.VAR_UNKNOWN})
+ eq(0, decode.json_decode_string(s, len, rettv))
+ eq(decode.VAR_UNKNOWN, rettv.v_type)
+ neq(nil, decode.last_msg_hist)
+ eq(msg, ffi.string(decode.last_msg_hist.msg))
+ end
+
+ it('does not overflow in error messages', function()
+ check_failure(']test', 1, 'E474: No container to close: ]')
+ check_failure('[}test', 2, 'E474: Closing list with curly bracket: }')
+ check_failure('{]test', 2,
+ 'E474: Closing dictionary with square bracket: ]')
+ check_failure('[1,]test', 4, 'E474: Trailing comma: ]')
+ check_failure('{"1":}test', 6, 'E474: Expected value after colon: }')
+ check_failure('{"1"}test', 5, 'E474: Expected value: }')
+ check_failure(',test', 1, 'E474: Comma not inside container: ,')
+ check_failure('[1,,1]test', 6, 'E474: Duplicate comma: ,1]')
+ check_failure('{"1":,}test', 7, 'E474: Comma after colon: ,}')
+ check_failure('{"1",}test', 6, 'E474: Using comma in place of colon: ,}')
+ check_failure('{,}test', 3, 'E474: Leading comma: ,}')
+ check_failure('[,]test', 3, 'E474: Leading comma: ,]')
+ check_failure(':test', 1, 'E474: Colon not inside container: :')
+ check_failure('[:]test', 3, 'E474: Using colon not in dictionary: :]')
+ check_failure('{:}test', 3, 'E474: Unexpected colon: :}')
+ check_failure('{"1"::1}test', 8, 'E474: Duplicate colon: :1}')
+ check_failure('ntest', 1, 'E474: Expected null: n')
+ check_failure('ttest', 1, 'E474: Expected true: t')
+ check_failure('ftest', 1, 'E474: Expected false: f')
+ check_failure('"\\test', 2, 'E474: Unfinished escape sequence: "\\')
+ check_failure('"\\u"test', 4,
+ 'E474: Unfinished unicode escape sequence: "\\u"')
+ check_failure('"\\uXXXX"est', 8,
+ 'E474: Expected four hex digits after \\u: \\uXXXX"')
+ check_failure('"\\?"test', 4, 'E474: Unknown escape sequence: \\?"')
+ check_failure(
+ '"\t"test', 3,
+ 'E474: ASCII control characters cannot be present inside string: \t"')
+ 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')
+ check_failure('?test', 1, 'E474: Unidentified byte: ?')
+ check_failure('1?test', 2, 'E474: Trailing characters: ?')
+ check_failure('[1test', 2, 'E474: Unexpected end of input: [1')
+ end)
+
+ it('does not overflow with `-`', function()
+ check_failure('-0', 1, 'E474: Missing number after minus sign: -')
+ end)
+
+ it('does not overflow and crash when running with `"`', function()
+ local rettv = ffi.new('typval_T', {v_type=decode.VAR_UNKNOWN})
+ decode.emsg_silent = 1
+ eq(0, decode.json_decode_string(char('"'), 1, rettv))
+ eq(decode.VAR_UNKNOWN, rettv.v_type)
+ end)
+end)
diff --git a/test/unit/eval/encode_spec.lua b/test/unit/eval/encode_spec.lua
new file mode 100644
index 0000000000..f151a191fb
--- /dev/null
+++ b/test/unit/eval/encode_spec.lua
@@ -0,0 +1,100 @@
+local helpers = require('test.unit.helpers')
+local eval_helpers = require('test.unit.eval.helpers')
+
+local cimport = helpers.cimport
+local to_cstr = helpers.to_cstr
+local eq = helpers.eq
+
+local list = eval_helpers.list
+local lst2tbl = eval_helpers.lst2tbl
+local type_key = eval_helpers.type_key
+local list_type = eval_helpers.list_type
+local null_string = eval_helpers.null_string
+
+local encode = cimport('./src/nvim/eval/encode.h')
+
+describe('encode_list_write()', function()
+ local encode_list_write = function(l, s)
+ return encode.encode_list_write(l, to_cstr(s), #s)
+ end
+
+ it('writes empty string', function()
+ local l = list()
+ eq(0, encode_list_write(l, ''))
+ eq({[type_key]=list_type}, lst2tbl(l))
+ end)
+
+ it('writes ASCII string literal with printable characters', function()
+ local l = list()
+ eq(0, encode_list_write(l, 'abc'))
+ eq({[type_key]=list_type, 'abc'}, lst2tbl(l))
+ end)
+
+ it('writes string starting with NL', function()
+ local l = list()
+ eq(0, encode_list_write(l, '\nabc'))
+ eq({[type_key]=list_type, null_string, 'abc'}, lst2tbl(l))
+ end)
+
+ it('writes string starting with NL twice', function()
+ local l = list()
+ eq(0, encode_list_write(l, '\nabc'))
+ eq({[type_key]=list_type, null_string, 'abc'}, lst2tbl(l))
+ eq(0, encode_list_write(l, '\nabc'))
+ eq({[type_key]=list_type, null_string, 'abc', 'abc'}, lst2tbl(l))
+ end)
+
+ it('writes string ending with NL', function()
+ local l = list()
+ eq(0, encode_list_write(l, 'abc\n'))
+ eq({[type_key]=list_type, 'abc', null_string}, lst2tbl(l))
+ end)
+
+ it('writes string ending with NL twice', function()
+ local l = list()
+ eq(0, encode_list_write(l, 'abc\n'))
+ eq({[type_key]=list_type, 'abc', null_string}, lst2tbl(l))
+ eq(0, encode_list_write(l, 'abc\n'))
+ eq({[type_key]=list_type, 'abc', 'abc', null_string}, lst2tbl(l))
+ end)
+
+ it('writes string starting, ending and containing NL twice', function()
+ local l = list()
+ eq(0, encode_list_write(l, '\na\nb\n'))
+ eq({[type_key]=list_type, null_string, 'a', 'b', null_string}, lst2tbl(l))
+ eq(0, encode_list_write(l, '\na\nb\n'))
+ eq({[type_key]=list_type, null_string, 'a', 'b', null_string, 'a', 'b', null_string}, lst2tbl(l))
+ end)
+
+ it('writes string starting, ending and containing NUL with NL between twice', function()
+ local l = list()
+ eq(0, encode_list_write(l, '\0\n\0\n\0'))
+ eq({[type_key]=list_type, '\n', '\n', '\n'}, lst2tbl(l))
+ eq(0, encode_list_write(l, '\0\n\0\n\0'))
+ eq({[type_key]=list_type, '\n', '\n', '\n\n', '\n', '\n'}, lst2tbl(l))
+ end)
+
+ it('writes string starting, ending and containing NL with NUL between twice', function()
+ local l = list()
+ eq(0, encode_list_write(l, '\n\0\n\0\n'))
+ eq({[type_key]=list_type, null_string, '\n', '\n', null_string}, lst2tbl(l))
+ eq(0, encode_list_write(l, '\n\0\n\0\n'))
+ eq({[type_key]=list_type, null_string, '\n', '\n', null_string, '\n', '\n', null_string}, lst2tbl(l))
+ end)
+
+ it('writes string containing a single NL twice', function()
+ local l = list()
+ eq(0, encode_list_write(l, '\n'))
+ eq({[type_key]=list_type, null_string, null_string}, lst2tbl(l))
+ eq(0, encode_list_write(l, '\n'))
+ eq({[type_key]=list_type, null_string, null_string, null_string}, lst2tbl(l))
+ end)
+
+ it('writes string containing a few NLs twice', function()
+ local l = list()
+ eq(0, encode_list_write(l, '\n\n\n'))
+ eq({[type_key]=list_type, null_string, null_string, null_string, null_string}, lst2tbl(l))
+ eq(0, encode_list_write(l, '\n\n\n'))
+ eq({[type_key]=list_type, null_string, null_string, null_string, null_string, null_string, null_string, null_string}, lst2tbl(l))
+ end)
+end)
diff --git a/test/unit/eval/helpers.lua b/test/unit/eval/helpers.lua
new file mode 100644
index 0000000000..da2f5626ff
--- /dev/null
+++ b/test/unit/eval/helpers.lua
@@ -0,0 +1,72 @@
+local helpers = require('test.unit.helpers')
+
+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/eval_defs.h')
+
+local null_string = {[true]='NULL string'}
+local null_list = {[true]='NULL list'}
+local type_key = {[true]='type key'}
+local list_type = {[true]='list type'}
+
+local list = function(...)
+ local ret = ffi.gc(eval.list_alloc(), eval.list_unref)
+ eq(0, ret.lv_refcount)
+ ret.lv_refcount = 1
+ for i = 1, select('#', ...) do
+ local val = select(i, ...)
+ local typ = type(val)
+ if typ == 'string' then
+ eval.list_append_string(ret, to_cstr(val))
+ elseif typ == 'table' and val == null_string then
+ eval.list_append_string(ret, nil)
+ elseif typ == 'table' and val == null_list then
+ eval.list_append_list(ret, nil)
+ elseif typ == 'table' and val[type_key] == list_type then
+ local itemlist = ffi.gc(list(table.unpack(val)), nil)
+ eq(1, itemlist.lv_refcount)
+ itemlist.lv_refcount = 0
+ eval.list_append_list(ret, itemlist)
+ else
+ assert(false, 'Not implemented yet')
+ end
+ end
+ return ret
+end
+
+local lst2tbl = function(l)
+ local ret = {[type_key]=list_type}
+ if l == nil then
+ return ret
+ end
+ local li = l.lv_first
+ -- (listitem_T *) NULL is equal to nil, but yet it is not false.
+ while li ~= nil do
+ local typ = li.li_tv.v_type
+ if typ == eval.VAR_STRING then
+ str = li.li_tv.vval.v_string
+ if str == nil then
+ ret[#ret + 1] = null_string
+ else
+ ret[#ret + 1] = ffi.string(str)
+ end
+ else
+ assert(false, 'Not implemented yet')
+ end
+ li = li.li_next
+ end
+ return ret
+end
+
+return {
+ null_string=null_string,
+ null_list=null_list,
+ list_type=list_type,
+ type_key=type_key,
+
+ list=list,
+ lst2tbl=lst2tbl,
+}
diff --git a/test/unit/eval/tricks_spec.lua b/test/unit/eval/tricks_spec.lua
new file mode 100644
index 0000000000..4c5184995c
--- /dev/null
+++ b/test/unit/eval/tricks_spec.lua
@@ -0,0 +1,43 @@
+local helpers = require('test.unit.helpers')
+
+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 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
+
+describe('NULL typval_T', function()
+ it('is produced by $XXX_UNEXISTENT_VAR_XXX', function()
+ -- Required for various tests which need to check whether typval_T with NULL
+ -- string works correctly. This test checks that unexistent environment
+ -- variable produces NULL string, not that some specific environment
+ -- variable does not exist. Last bit is left for the test writers.
+ local unexistent_env = 'XXX_UNEXISTENT_VAR_XXX'
+ while os.getenv(unexistent_env) ~= nil do
+ unexistent_env = unexistent_env .. '_XXX'
+ end
+ local rettv = eval_expr('$' .. unexistent_env)
+ eq(eval.VAR_STRING, rettv.v_type)
+ eq(nil, rettv.vval.v_string)
+ end)
+
+ it('is produced by v:_null_list', function()
+ local rettv = eval_expr('v:_null_list')
+ eq(eval.VAR_LIST, rettv.v_type)
+ eq(nil, rettv.vval.v_list)
+ end)
+
+ it('is produced by v:_null_dict', function()
+ local rettv = eval_expr('v:_null_dict')
+ eq(eval.VAR_DICT, rettv.v_type)
+ eq(nil, rettv.vval.v_dict)
+ end)
+end)
diff --git a/test/unit/formatc.lua b/test/unit/formatc.lua
index 3f86c5f1b1..00637e0b8d 100644
--- a/test/unit/formatc.lua
+++ b/test/unit/formatc.lua
@@ -238,7 +238,7 @@ local function standalone(...) -- luacheck: ignore
end
-- uncomment this line (and comment the `return`) for standalone debugging
-- example usage:
--- ../../.deps/usr/bin/luajit formatc.lua ../../include/tempfile.h.generated.h
+-- ../../.deps/usr/bin/luajit formatc.lua ../../include/fileio.h.generated.h
-- ../../.deps/usr/bin/luajit formatc.lua /usr/include/malloc.h
-- standalone(...)
return formatc
diff --git a/test/unit/helpers.lua b/test/unit/helpers.lua
index 7b43b2218c..426ae2d9e0 100644
--- a/test/unit/helpers.lua
+++ b/test/unit/helpers.lua
@@ -28,8 +28,10 @@ local function filter_complex_blocks(body)
local result = {}
for line in body:gmatch("[^\r\n]+") do
- if not (string.find(line, "(^)", 1, true) ~= nil or
- string.find(line, "_ISwupper", 1, true)) then
+ if not (string.find(line, "(^)", 1, true) ~= nil
+ or string.find(line, "_ISwupper", 1, true)
+ or string.find(line, "msgpack_zone_push_finalizer")
+ or string.find(line, "msgpack_unpacker_reserve_buffer")) then
result[#result + 1] = line
end
end
@@ -103,6 +105,11 @@ local function cimport(...)
-- request a sorted version of the new lines (same relative order as the
-- original preprocessed file) and feed that to the LuaJIT ffi
local new_lines = new_cdefs:to_table()
+ if os.getenv('NVIM_TEST_PRINT_CDEF') == '1' then
+ for lnum, line in ipairs(new_lines) do
+ print(lnum, line)
+ end
+ end
ffi.cdef(table.concat(new_lines, "\n"))
return libnvim
@@ -133,6 +140,7 @@ do
local time = cimport('./src/nvim/os/time.h')
time.time_init()
main.early_init()
+ main.event_init()
end
-- C constants.
diff --git a/test/unit/os/shell_spec.lua b/test/unit/os/shell_spec.lua
index 6d1a9f3589..93103e4e8c 100644
--- a/test/unit/os/shell_spec.lua
+++ b/test/unit/os/shell_spec.lua
@@ -11,7 +11,7 @@ if allowed_os[jit.os] ~= true then
end
local helpers = require('test.unit.helpers')
-local shell = helpers.cimport(
+local cimported = helpers.cimport(
'./src/nvim/os/shell.h',
'./src/nvim/option_defs.h',
'./src/nvim/main.h',
@@ -25,18 +25,17 @@ local NULL = ffi.cast('void *', 0)
describe('shell functions', function()
setup(function()
- shell.event_init()
-- os_system() can't work when the p_sh and p_shcf variables are unset
- shell.p_sh = to_cstr('/bin/bash')
- shell.p_shcf = to_cstr('-c')
+ cimported.p_sh = to_cstr('/bin/bash')
+ cimported.p_shcf = to_cstr('-c')
end)
teardown(function()
- shell.event_teardown()
+ cimported.event_teardown()
end)
local function shell_build_argv(cmd, extra_args)
- local res = shell.shell_build_argv(
+ local res = cimported.shell_build_argv(
cmd and to_cstr(cmd),
extra_args and to_cstr(extra_args))
local argc = 0
@@ -45,10 +44,10 @@ describe('shell functions', function()
-- crash.
while res[argc] ~= nil do
ret[#ret + 1] = ffi.string(res[argc])
- shell.xfree(res[argc])
+ cimported.xfree(res[argc])
argc = argc + 1
end
- shell.xfree(res)
+ cimported.xfree(res)
return ret
end
@@ -59,8 +58,8 @@ describe('shell functions', function()
local nread = ffi.new('size_t[1]')
local argv = ffi.cast('char**',
- shell.shell_build_argv(to_cstr(cmd), nil))
- local status = shell.os_system(argv, input_or, input_len, output, nread)
+ cimported.shell_build_argv(to_cstr(cmd), nil))
+ local status = cimported.os_system(argv, input_or, input_len, output, nread)
return status, intern(output[0], nread[0])
end
@@ -97,13 +96,13 @@ describe('shell functions', function()
local saved_opts = {}
setup(function()
- saved_opts.p_sh = shell.p_sh
- saved_opts.p_shcf = shell.p_shcf
+ saved_opts.p_sh = cimported.p_sh
+ saved_opts.p_shcf = cimported.p_shcf
end)
teardown(function()
- shell.p_sh = saved_opts.p_sh
- shell.p_shcf = saved_opts.p_shcf
+ cimported.p_sh = saved_opts.p_sh
+ cimported.p_shcf = saved_opts.p_shcf
end)
it('works with NULL arguments', function()
@@ -123,8 +122,8 @@ describe('shell functions', function()
end)
it('splits and unquotes &shell and &shellcmdflag', function()
- shell.p_sh = to_cstr('/Program" "Files/zsh -f')
- shell.p_shcf = to_cstr('-x -o "sh word split" "-"c')
+ cimported.p_sh = to_cstr('/Program" "Files/zsh -f')
+ cimported.p_shcf = to_cstr('-x -o "sh word split" "-"c')
eq({'/Program Files/zsh', '-f',
'ghi jkl',
'-x', '-o', 'sh word split',
diff --git a/test/unit/tempfile_spec.lua b/test/unit/tempfile_spec.lua
index e558ff04c8..b3e84db132 100644
--- a/test/unit/tempfile_spec.lua
+++ b/test/unit/tempfile_spec.lua
@@ -2,7 +2,7 @@ local lfs = require 'lfs'
local helpers = require 'test.unit.helpers'
local os = helpers.cimport './src/nvim/os/os.h'
-local tempfile = helpers.cimport './src/nvim/tempfile.h'
+local tempfile = helpers.cimport './src/nvim/fileio.h'
describe('tempfile related functions', function()
after_each(function()