diff options
Diffstat (limited to 'test/unit')
-rw-r--r-- | test/unit/eval/decode_spec.lua | 142 | ||||
-rw-r--r-- | test/unit/eval/encode_spec.lua | 100 | ||||
-rw-r--r-- | test/unit/eval/helpers.lua | 72 | ||||
-rw-r--r-- | test/unit/eval/tricks_spec.lua | 43 | ||||
-rw-r--r-- | test/unit/formatc.lua | 2 | ||||
-rw-r--r-- | test/unit/helpers.lua | 12 | ||||
-rw-r--r-- | test/unit/os/shell_spec.lua | 31 | ||||
-rw-r--r-- | test/unit/tempfile_spec.lua | 2 |
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() |