diff options
Diffstat (limited to 'test/unit')
-rw-r--r-- | test/unit/buffer_spec.lua | 108 | ||||
-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/fileio_spec.lua | 1 | ||||
-rw-r--r-- | test/unit/formatc.lua | 14 | ||||
-rw-r--r-- | test/unit/garray_spec.lua | 19 | ||||
-rw-r--r-- | test/unit/helpers.lua | 28 | ||||
-rw-r--r-- | test/unit/mbyte_spec.lua | 276 | ||||
-rw-r--r-- | test/unit/os/env_spec.lua | 12 | ||||
-rw-r--r-- | test/unit/os/fs_spec.lua | 53 | ||||
-rw-r--r-- | test/unit/os/shell_spec.lua | 77 | ||||
-rw-r--r-- | test/unit/os/users_spec.lua | 8 | ||||
-rw-r--r-- | test/unit/path_spec.lua | 80 | ||||
-rw-r--r-- | test/unit/preprocess.lua | 4 | ||||
-rw-r--r-- | test/unit/profile_spec.lua | 26 | ||||
-rw-r--r-- | test/unit/set.lua | 2 | ||||
-rw-r--r-- | test/unit/strings_spec.lua | 68 | ||||
-rw-r--r-- | test/unit/tempfile_spec.lua | 5 |
20 files changed, 988 insertions, 150 deletions
diff --git a/test/unit/buffer_spec.lua b/test/unit/buffer_spec.lua index e0e2b827e9..b7f82064d7 100644 --- a/test/unit/buffer_spec.lua +++ b/test/unit/buffer_spec.lua @@ -1,11 +1,14 @@ + +local assert = require("luassert") local helpers = require("test.unit.helpers") local to_cstr = helpers.to_cstr local eq = helpers.eq +local neq = helpers.neq +local NULL = helpers.NULL +local globals = helpers.cimport("./src/nvim/globals.h") local buffer = helpers.cimport("./src/nvim/buffer.h") -local window = helpers.cimport("./src/nvim/window.h") -local option = helpers.cimport("./src/nvim/option.h") describe('buffer functions', function() @@ -38,13 +41,13 @@ describe('buffer functions', function() describe('buf_valid', function() it('should view NULL as an invalid buffer', function() - eq(0, buffer.buf_valid(NULL)) + eq(false, buffer.buf_valid(NULL)) end) it('should view an open buffer as valid', function() local buf = buflist_new(path1, buffer.BLN_LISTED) - eq(1, buffer.buf_valid(buf)) + eq(true, buffer.buf_valid(buf)) end) it('should view a closed and hidden buffer as valid', function() @@ -52,7 +55,7 @@ describe('buffer functions', function() close_buffer(NULL, buf, 0, 0) - eq(1, buffer.buf_valid(buf)) + eq(true, buffer.buf_valid(buf)) end) it('should view a closed and unloaded buffer as valid', function() @@ -60,7 +63,7 @@ describe('buffer functions', function() close_buffer(NULL, buf, buffer.DOBUF_UNLOAD, 0) - eq(1, buffer.buf_valid(buf)) + eq(true, buffer.buf_valid(buf)) end) it('should view a closed and wiped buffer as invalid', function() @@ -68,7 +71,7 @@ describe('buffer functions', function() close_buffer(NULL, buf, buffer.DOBUF_WIPE, 0) - eq(0, buffer.buf_valid(buf)) + eq(false, buffer.buf_valid(buf)) end) end) @@ -206,4 +209,95 @@ describe('buffer functions', function() close_buffer(NULL, buf2, buffer.DOBUF_WIPE, 0) end) end) + + describe('build_stl_str_hl', function() + + local output_buffer = to_cstr(string.rep(" ", 100)) + + local build_stl_str_hl = function(pat) + return buffer.build_stl_str_hl(globals.curwin, + output_buffer, + 100, + to_cstr(pat), + false, + 32, + 80, + NULL, + NULL) + end + + it('should copy plain text', function() + local width = build_stl_str_hl("this is a test") + + eq(14, width) + eq("this is a test", helpers.ffi.string(output_buffer, width)) + + end) + + it('should print no file name', function() + local width = build_stl_str_hl("%f") + + eq(9, width) + eq("[No Name]", helpers.ffi.string(output_buffer, width)) + + end) + + it('should print the relative file name', function() + buffer.setfname(globals.curbuf, to_cstr("Makefile"), NULL, 1) + local width = build_stl_str_hl("%f") + + eq(8, width) + eq("Makefile", helpers.ffi.string(output_buffer, width)) + + end) + + it('should print the full file name', function() + buffer.setfname(globals.curbuf, to_cstr("Makefile"), NULL, 1) + + local width = build_stl_str_hl("%F") + + assert.is_true(8 < width) + neq(NULL, string.find(helpers.ffi.string(output_buffer, width), "Makefile")) + + end) + + it('should print the tail file name', function() + buffer.setfname(globals.curbuf, to_cstr("src/nvim/buffer.c"), NULL, 1) + + local width = build_stl_str_hl("%t") + + eq(8, width) + eq("buffer.c", helpers.ffi.string(output_buffer, width)) + + end) + + it('should print the buffer number', function() + buffer.setfname(globals.curbuf, to_cstr("src/nvim/buffer.c"), NULL, 1) + + local width = build_stl_str_hl("%n") + + eq(1, width) + eq("1", helpers.ffi.string(output_buffer, width)) + end) + + it('should print the current line number in the buffer', function() + buffer.setfname(globals.curbuf, to_cstr("test/unit/buffer_spec.lua"), NULL, 1) + + local width = build_stl_str_hl("%l") + + eq(1, width) + eq("0", helpers.ffi.string(output_buffer, width)) + + end) + + it('should print the number of lines in the buffer', function() + buffer.setfname(globals.curbuf, to_cstr("test/unit/buffer_spec.lua"), NULL, 1) + + local width = build_stl_str_hl("%L") + + eq(1, width) + eq("1", helpers.ffi.string(output_buffer, width)) + + end) + end) end) 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..2367f03e0d --- /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 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 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 + local 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/fileio_spec.lua b/test/unit/fileio_spec.lua index 180fc3c184..3e3c36617d 100644 --- a/test/unit/fileio_spec.lua +++ b/test/unit/fileio_spec.lua @@ -4,6 +4,7 @@ local helpers = require("test.unit.helpers") local eq = helpers.eq local ffi = helpers.ffi local to_cstr = helpers.to_cstr +local NULL = helpers.NULL local fileio = helpers.cimport("./src/nvim/fileio.h") diff --git a/test/unit/formatc.lua b/test/unit/formatc.lua index f9397eaec6..00637e0b8d 100644 --- a/test/unit/formatc.lua +++ b/test/unit/formatc.lua @@ -124,13 +124,13 @@ end local function set(t) local s = {} - for i, v in ipairs(t) do + for _, v in ipairs(t) do s[v] = true end return s end -local C_keywords = set { +local C_keywords = set { -- luacheck: ignore "break", "case", "char", "const", "continue", "default", "do", "double", "else", "enum", "extern", "float", "for", "goto", "if", "int", "long", "register", "return", "short", "signed", "sizeof", "static", "struct", @@ -154,13 +154,13 @@ local C_keywords = set { -- The first one will have a lot of false positives (the line '{' for -- example), the second one is more unique. local function formatc(str) - local tokens = TokeniseC(str) + local toks = TokeniseC(str) local result = {} local block_level = 0 local allow_one_nl = false local end_at_brace = false - for i, token in ipairs(tokens) do + for _, token in ipairs(toks) do local typ = token[2] if typ == '{' then block_level = block_level + 1 @@ -213,8 +213,8 @@ local function formatc(str) end -- standalone operation (very handy for debugging) -local function standalone(...) - Preprocess = require("preprocess") +local function standalone(...) -- luacheck: ignore + local Preprocess = require("preprocess") Preprocess.add_to_include_path('./../../src') Preprocess.add_to_include_path('./../../build/include') Preprocess.add_to_include_path('./../../.deps/usr/include') @@ -238,7 +238,7 @@ local function standalone(...) 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/garray_spec.lua b/test/unit/garray_spec.lua index e779cab8a7..9694e3c427 100644 --- a/test/unit/garray_spec.lua +++ b/test/unit/garray_spec.lua @@ -5,8 +5,6 @@ local internalize = helpers.internalize local eq = helpers.eq local neq = helpers.neq local ffi = helpers.ffi -local lib = helpers.lib -local cstr = helpers.cstr local to_cstr = helpers.to_cstr local NULL = helpers.NULL @@ -48,7 +46,7 @@ local ga_size = function(garr) return ga_len(garr) * ga_itemsize(garr) end -local ga_maxsize = function(garr) +local ga_maxsize = function(garr) -- luacheck: ignore return ga_maxlen(garr) * ga_itemsize(garr) end @@ -65,8 +63,8 @@ local ga_data_as_ints = function(garr) end -- garray manipulation -local ga_init = function(garr, itemsize, growsize) - return garray.ga_init(garr, itemsize, growsize) +local ga_init = function(garr, itemsize_, growsize_) + return garray.ga_init(garr, itemsize_, growsize_) end local ga_clear = function(garr) @@ -113,7 +111,7 @@ local ga_set_len = function(garr, len) end local ga_inc_len = function(garr, by) - return ga_set_len(garr, ga_len(garr) + 1) + return ga_set_len(garr, ga_len(garr) + by) end -- custom append functions @@ -197,10 +195,9 @@ describe('garray', function() end) describe('ga_grow', function() - local new_and_grow - function new_and_grow(itemsize, growsize, req) + local function new_and_grow(itemsize_, growsize_, req) local garr = new_garray() - ga_init(garr, itemsize, growsize) + ga_init(garr, itemsize_, growsize_) eq(0, ga_size(garr)) -- should be 0 at first eq(NULL, ga_data(garr)) -- should be NULL ga_grow(garr, req) -- add space for `req` items @@ -306,7 +303,7 @@ describe('garray', function() ga_init(garr, ffi.sizeof("char"), 1) local str = "ohwell●●" local loop = 5 - for i = 1, loop do + for _ = 1, loop do ga_concat(garr, str) end @@ -321,7 +318,7 @@ describe('garray', function() end) end) - function test_concat_fn(input, fn, sep) + local function test_concat_fn(input, fn, sep) local garr = new_string_garray() ga_append_strings(garr, unpack(input)) if sep == nil then diff --git a/test/unit/helpers.lua b/test/unit/helpers.lua index 5bcc661226..426ae2d9e0 100644 --- a/test/unit/helpers.lua +++ b/test/unit/helpers.lua @@ -18,17 +18,9 @@ local function trim(s) end -- a Set that keeps around the lines we've already seen -if cdefs == nil then - cdefs = Set:new() -end - -if imported == nil then - imported = Set:new() -end - -if pragma_pack_id == nil then - pragma_pack_id = 1 -end +local cdefs = Set:new() +local imported = Set:new() +local pragma_pack_id = 1 -- some things are just too complex for the LuaJIT C parser to digest. We -- usually don't need them anyway. @@ -36,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 @@ -67,7 +61,7 @@ local function cimport(...) end local body = nil - for i=1, 10 do + for _ = 1, 10 do local stream = Preprocess.preprocess_stream(unpack(paths)) body = stream:read("*a") stream:close() @@ -111,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 @@ -141,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/mbyte_spec.lua b/test/unit/mbyte_spec.lua new file mode 100644 index 0000000000..9b2415a93f --- /dev/null +++ b/test/unit/mbyte_spec.lua @@ -0,0 +1,276 @@ +local helpers = require("test.unit.helpers") + +local ffi = helpers.ffi +local eq = helpers.eq + +local mbyte = helpers.cimport("./src/nvim/mbyte.h") + +describe('mbyte', function() + + -- Array for composing characters + local intp = ffi.typeof('int[?]') + local function to_intp() + -- how to get MAX_MCO from globals.h? + return intp(7, 1) + end + + -- Convert from bytes to string + local function to_string(bytes) + local s = {} + for i = 1, #bytes do + s[i] = string.char(bytes[i]) + end + return table.concat(s) + end + + before_each(function() + end) + + it('utf_ptr2char', function() + -- For strings with length 1 the first byte is returned. + for c = 0, 255 do + eq(c, mbyte.utf_ptr2char(to_string({c, 0}))) + end + + -- Some ill formed byte sequences that should not be recognized as UTF-8 + -- First byte: 0xc0 or 0xc1 + -- Second byte: 0x80 .. 0xbf + --eq(0x00c0, mbyte.utf_ptr2char(to_string({0xc0, 0x80}))) + --eq(0x00c1, mbyte.utf_ptr2char(to_string({0xc1, 0xbf}))) + -- + -- Sequences with more than four bytes + end) + + + describe('utfc_ptr2char_len', function() + + it('1-byte sequences', function() + local pcc = to_intp() + for c = 0, 255 do + eq(c, mbyte.utfc_ptr2char_len(to_string({c}), pcc, 1)) + eq(0, pcc[0]) + end + end) + + it('2-byte sequences', function() + local pcc = to_intp() + -- No combining characters + eq(0x007f, mbyte.utfc_ptr2char_len(to_string({0x7f, 0x7f}), pcc, 2)) + eq(0, pcc[0]) + -- No combining characters + pcc = to_intp() + eq(0x007f, mbyte.utfc_ptr2char_len(to_string({0x7f, 0x80}), pcc, 2)) + eq(0, pcc[0]) + + -- No UTF-8 sequence + pcc = to_intp() + eq(0x00c2, mbyte.utfc_ptr2char_len(to_string({0xc2, 0x7f}), pcc, 2)) + eq(0, pcc[0]) + -- One UTF-8 character + pcc = to_intp() + eq(0x0080, mbyte.utfc_ptr2char_len(to_string({0xc2, 0x80}), pcc, 2)) + eq(0, pcc[0]) + -- No UTF-8 sequence + pcc = to_intp() + eq(0x00c2, mbyte.utfc_ptr2char_len(to_string({0xc2, 0xc0}), pcc, 2)) + eq(0, pcc[0]) + end) + + it('3-byte sequences', function() + local pcc = to_intp() + + -- No second UTF-8 character + eq(0x007f, mbyte.utfc_ptr2char_len(to_string({0x7f, 0x80, 0x80}), pcc, 3)) + eq(0, pcc[0]) + -- No combining character + pcc = to_intp() + eq(0x007f, mbyte.utfc_ptr2char_len(to_string({0x7f, 0xc2, 0x80}), pcc, 3)) + eq(0, pcc[0]) + + -- Combining character is U+0300 + pcc = to_intp() + eq(0x007f, mbyte.utfc_ptr2char_len(to_string({0x7f, 0xcc, 0x80}), pcc, 3)) + eq(0x0300, pcc[0]) + eq(0x0000, pcc[1]) + + -- No UTF-8 sequence + pcc = to_intp() + eq(0x00c2, mbyte.utfc_ptr2char_len(to_string({0xc2, 0x7f, 0xcc}), pcc, 3)) + eq(0, pcc[0]) + -- Incomplete combining character + pcc = to_intp() + eq(0x0080, mbyte.utfc_ptr2char_len(to_string({0xc2, 0x80, 0xcc}), pcc, 3)) + eq(0, pcc[0]) + + -- One UTF-8 character + pcc = to_intp() + eq(0x20d0, mbyte.utfc_ptr2char_len(to_string({0xe2, 0x83, 0x90}), pcc, 3)) + eq(0, pcc[0]) + end) + + it('4-byte sequences', function() + local pcc = to_intp() + + -- No following combining character + eq(0x007f, mbyte.utfc_ptr2char_len(to_string({0x7f, 0x7f, 0xcc, 0x80}), pcc, 4)) + eq(0, pcc[0]) + -- No second UTF-8 character + pcc = to_intp() + eq(0x007f, mbyte.utfc_ptr2char_len(to_string({0x7f, 0xc2, 0xcc, 0x80}), pcc, 4)) + eq(0, pcc[0]) + + -- Combining character U+0300 + pcc = to_intp() + eq(0x007f, mbyte.utfc_ptr2char_len(to_string({0x7f, 0xcc, 0x80, 0xcc}), pcc, 4)) + eq(0x0300, pcc[0]) + eq(0x0000, pcc[1]) + + -- No UTF-8 sequence + pcc = to_intp() + eq(0x00c2, mbyte.utfc_ptr2char_len(to_string({0xc2, 0x7f, 0xcc, 0x80}), pcc, 4)) + eq(0, pcc[0]) + -- No following UTF-8 character + pcc = to_intp() + eq(0x0080, mbyte.utfc_ptr2char_len(to_string({0xc2, 0x80, 0xcc, 0xcc}), pcc, 4)) + eq(0, pcc[0]) + -- Combining character U+0301 + pcc = to_intp() + eq(0x0080, mbyte.utfc_ptr2char_len(to_string({0xc2, 0x80, 0xcc, 0x81}), pcc, 4)) + eq(0x0301, pcc[0]) + eq(0x0000, pcc[1]) + + -- One UTF-8 character + pcc = to_intp() + eq(0x100000, mbyte.utfc_ptr2char_len(to_string({0xf4, 0x80, 0x80, 0x80}), pcc, 4)) + eq(0, pcc[0]) + end) + + it('5+-byte sequences', function() + local pcc = to_intp() + + -- No following combining character + eq(0x007f, mbyte.utfc_ptr2char_len(to_string({0x7f, 0x7f, 0xcc, 0x80, 0x80}), pcc, 5)) + eq(0, pcc[0]) + -- No second UTF-8 character + pcc = to_intp() + eq(0x007f, mbyte.utfc_ptr2char_len(to_string({0x7f, 0xc2, 0xcc, 0x80, 0x80}), pcc, 5)) + eq(0, pcc[0]) + + -- Combining character U+0300 + pcc = to_intp() + eq(0x007f, mbyte.utfc_ptr2char_len(to_string({0x7f, 0xcc, 0x80, 0xcc}), pcc, 5)) + eq(0x0300, pcc[0]) + eq(0x0000, pcc[1]) + + -- Combining characters U+0300 and U+0301 + pcc = to_intp() + eq(0x007f, mbyte.utfc_ptr2char_len(to_string({0x7f, 0xcc, 0x80, 0xcc, 0x81}), pcc, 5)) + eq(0x0300, pcc[0]) + eq(0x0301, pcc[1]) + eq(0x0000, pcc[2]) + -- Combining characters U+0300, U+0301, U+0302 + pcc = to_intp() + eq(0x007f, mbyte.utfc_ptr2char_len(to_string({0x7f, 0xcc, 0x80, 0xcc, 0x81, 0xcc, 0x82}), pcc, 7)) + eq(0x0300, pcc[0]) + eq(0x0301, pcc[1]) + eq(0x0302, pcc[2]) + eq(0x0000, pcc[3]) + -- Combining characters U+0300, U+0301, U+0302, U+0303 + pcc = to_intp() + eq(0x007f, mbyte.utfc_ptr2char_len(to_string({0x7f, 0xcc, 0x80, 0xcc, 0x81, 0xcc, 0x82, 0xcc, 0x83}), pcc, 9)) + eq(0x0300, pcc[0]) + eq(0x0301, pcc[1]) + eq(0x0302, pcc[2]) + eq(0x0303, pcc[3]) + eq(0x0000, pcc[4]) + -- Combining characters U+0300, U+0301, U+0302, U+0303, U+0304 + pcc = to_intp() + eq(0x007f, mbyte.utfc_ptr2char_len(to_string( + {0x7f, 0xcc, 0x80, 0xcc, 0x81, 0xcc, 0x82, 0xcc, 0x83, 0xcc, 0x84}), pcc, 11)) + eq(0x0300, pcc[0]) + eq(0x0301, pcc[1]) + eq(0x0302, pcc[2]) + eq(0x0303, pcc[3]) + eq(0x0304, pcc[4]) + eq(0x0000, pcc[5]) + -- Combining characters U+0300, U+0301, U+0302, U+0303, U+0304, + -- U+0305 + pcc = to_intp() + eq(0x007f, mbyte.utfc_ptr2char_len(to_string( + {0x7f, 0xcc, 0x80, 0xcc, 0x81, 0xcc, 0x82, 0xcc, 0x83, 0xcc, 0x84, 0xcc, 0x85}), pcc, 13)) + eq(0x0300, pcc[0]) + eq(0x0301, pcc[1]) + eq(0x0302, pcc[2]) + eq(0x0303, pcc[3]) + eq(0x0304, pcc[4]) + eq(0x0305, pcc[5]) + eq(1, pcc[6]) + + -- Combining characters U+0300, U+0301, U+0302, U+0303, U+0304, + -- U+0305, U+0306, but only save six (= MAX_MCO). + pcc = to_intp() + eq(0x007f, mbyte.utfc_ptr2char_len(to_string( + {0x7f, 0xcc, 0x80, 0xcc, 0x81, 0xcc, 0x82, 0xcc, 0x83, 0xcc, 0x84, 0xcc, 0x85, 0xcc, 0x86}), pcc, 15)) + eq(0x0300, pcc[0]) + eq(0x0301, pcc[1]) + eq(0x0302, pcc[2]) + eq(0x0303, pcc[3]) + eq(0x0304, pcc[4]) + eq(0x0305, pcc[5]) + eq(0x0001, pcc[6]) + + -- Only three following combining characters U+0300, U+0301, U+0302 + pcc = to_intp() + eq(0x007f, mbyte.utfc_ptr2char_len(to_string( + {0x7f, 0xcc, 0x80, 0xcc, 0x81, 0xcc, 0x82, 0xc2, 0x80, 0xcc, 0x84, 0xcc, 0x85}), pcc, 13)) + eq(0x0300, pcc[0]) + eq(0x0301, pcc[1]) + eq(0x0302, pcc[2]) + eq(0x0000, pcc[3]) + + + -- No UTF-8 sequence + pcc = to_intp() + eq(0x00c2, mbyte.utfc_ptr2char_len(to_string({0xc2, 0x7f, 0xcc, 0x80, 0x80}), pcc, 5)) + eq(0, pcc[0]) + -- No following UTF-8 character + pcc = to_intp() + eq(0x0080, mbyte.utfc_ptr2char_len(to_string({0xc2, 0x80, 0xcc, 0xcc, 0x80}), pcc, 5)) + eq(0, pcc[0]) + -- Combining character U+0301 + pcc = to_intp() + eq(0x0080, mbyte.utfc_ptr2char_len(to_string({0xc2, 0x80, 0xcc, 0x81, 0x7f}), pcc, 5)) + eq(0x0301, pcc[0]) + eq(0x0000, pcc[1]) + -- Combining character U+0301 + pcc = to_intp() + eq(0x0080, mbyte.utfc_ptr2char_len(to_string({0xc2, 0x80, 0xcc, 0x81, 0xcc}), pcc, 5)) + eq(0x0301, pcc[0]) + eq(0x0000, pcc[1]) + + -- One UTF-8 character + pcc = to_intp() + eq(0x100000, mbyte.utfc_ptr2char_len(to_string({0xf4, 0x80, 0x80, 0x80, 0x7f}), pcc, 5)) + eq(0, pcc[0]) + + -- One UTF-8 character + pcc = to_intp() + eq(0x100000, mbyte.utfc_ptr2char_len(to_string({0xf4, 0x80, 0x80, 0x80, 0x80}), pcc, 5)) + eq(0, pcc[0]) + -- One UTF-8 character + pcc = to_intp() + eq(0x100000, mbyte.utfc_ptr2char_len(to_string({0xf4, 0x80, 0x80, 0x80, 0xcc}), pcc, 5)) + eq(0, pcc[0]) + + -- Combining characters U+1AB0 and U+0301 + pcc = to_intp() + eq(0x100000, mbyte.utfc_ptr2char_len(to_string( + {0xf4, 0x80, 0x80, 0x80, 0xe1, 0xaa, 0xb0, 0xcc, 0x81}), pcc, 9)) + eq(0x1ab0, pcc[0]) + eq(0x0301, pcc[1]) + eq(0x0000, pcc[2]) + end) + + end) + +end) diff --git a/test/unit/os/env_spec.lua b/test/unit/os/env_spec.lua index 8e18c599d9..9e00a3e8f8 100644 --- a/test/unit/os/env_spec.lua +++ b/test/unit/os/env_spec.lua @@ -1,11 +1,9 @@ local helpers = require('test.unit.helpers') local cimport = helpers.cimport -local internalize = helpers.internalize local eq = helpers.eq local neq = helpers.neq local ffi = helpers.ffi -local lib = helpers.lib local cstr = helpers.cstr local to_cstr = helpers.to_cstr local NULL = helpers.NULL @@ -15,15 +13,15 @@ require('lfs') local env = cimport('./src/nvim/os/os.h') describe('env function', function() - function os_setenv(name, value, override) + local function os_setenv(name, value, override) return env.os_setenv((to_cstr(name)), (to_cstr(value)), override) end - function os_unsetenv(name, value, override) + local function os_unsetenv(name, _, _) return env.os_unsetenv((to_cstr(name))) end - function os_getenv(name) + local function os_getenv(name) local rval = env.os_getenv((to_cstr(name))) if rval ~= NULL then return ffi.string(rval) @@ -150,8 +148,8 @@ describe('env function', function() local name = 'NEOVIM_UNIT_TEST_EXPAND_ENV_ESCN' local value = 'NEOVIM_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 + -- 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 output_buff1 = cstr(255, '') diff --git a/test/unit/os/fs_spec.lua b/test/unit/os/fs_spec.lua index c7a1f55b5d..71b5e7f576 100644 --- a/test/unit/os/fs_spec.lua +++ b/test/unit/os/fs_spec.lua @@ -1,3 +1,6 @@ +local lfs = require('lfs') +local bit = require('bit') + local helpers = require('test.unit.helpers') local cimport = helpers.cimport @@ -6,16 +9,12 @@ local internalize = helpers.internalize local eq = helpers.eq local neq = helpers.neq local ffi = helpers.ffi -local lib = helpers.lib local cstr = helpers.cstr local to_cstr = helpers.to_cstr local OK = helpers.OK local FAIL = helpers.FAIL local NULL = helpers.NULL -require('lfs') -require('bit') - cimport('unistd.h') cimport('./src/nvim/os/shell.h') cimport('./src/nvim/option_defs.h') @@ -23,11 +22,10 @@ cimport('./src/nvim/main.h') cimport('./src/nvim/fileio.h') local fs = cimport('./src/nvim/os/os.h') cppimport('sys/stat.h') -cppimport('sys/fcntl.h') -cppimport('sys/errno.h') +cppimport('fcntl.h') +cppimport('uv-errno.h') -local len = 0 -local buf = "" +local buffer = "" local directory = nil local absolute_executable = nil local executable_name = nil @@ -85,24 +83,26 @@ 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() - len = (string.len(lfs.currentdir())) + 1 - buf = cstr(len, '') + length = (string.len(lfs.currentdir())) + 1 + buffer = cstr(length, '') end) it('returns OK and writes current directory into the buffer if it is large\n enough', function() - eq(OK, (os_dirname(buf, len))) - eq(lfs.currentdir(), (ffi.string(buf))) + eq(OK, (os_dirname(buffer, length))) + eq(lfs.currentdir(), (ffi.string(buffer))) end) -- What kind of other failing cases are possible? it('returns FAIL if the buffer is too small', function() - local buf = cstr((len - 1), '') - eq(FAIL, (os_dirname(buf, (len - 1)))) + local buf = cstr((length - 1), '') + eq(FAIL, (os_dirname(buf, (length - 1)))) end) end) @@ -148,7 +148,7 @@ describe('fs function', function() local function os_can_exe(name) local buf = ffi.new('char *[1]') buf[0] = NULL - local ok = fs.os_can_exe(to_cstr(name), buf) + local ok = fs.os_can_exe(to_cstr(name), buf, true) -- When os_can_exe returns true, it must set the path. -- When it returns false, the path must be NULL. @@ -213,15 +213,6 @@ describe('fs function', function() os_setperm('unit-test-directory/test.file', orig_test_file_perm) end) - local function os_getperm(filename) - local perm = fs.os_getperm((to_cstr(filename))) - return tonumber(perm) - end - - local function os_setperm(filename, perm) - return fs.os_setperm((to_cstr(filename)), perm) - end - local function os_fchown(filename, user_id, group_id) local fd = ffi.C.open(filename, 0) local res = fs.os_fchown(fd, user_id, group_id) @@ -242,8 +233,8 @@ describe('fs function', function() end describe('os_getperm', function() - it('returns -1 when the given file does not exist', function() - eq(-1, (os_getperm('non-existing-file'))) + it('returns UV_ENOENT when the given file does not exist', function() + eq(ffi.C.UV_ENOENT, (os_getperm('non-existing-file'))) end) it('returns a perm > 0 when given an existing file', function() @@ -454,8 +445,8 @@ describe('fs function', function() local new_file = 'test_new_file' local existing_file = 'unit-test-directory/test_existing.file' - it('returns -ENOENT for O_RDWR on a non-existing file', function() - eq(-ffi.C.kENOENT, (os_open('non-existing-file', ffi.C.kO_RDWR, 0))) + it('returns UV_ENOENT for O_RDWR on a non-existing file', function() + eq(ffi.C.UV_ENOENT, (os_open('non-existing-file', ffi.C.kO_RDWR, 0))) end) it('returns non-negative for O_CREAT on a non-existing file', function() @@ -468,9 +459,9 @@ describe('fs function', function() assert.is_true(0 <= (os_open(existing_file, ffi.C.kO_CREAT, 0))) end) - it('returns -EEXIST for O_CREAT|O_EXCL on a existing file', function() + it('returns UV_EEXIST for O_CREAT|O_EXCL on a existing file', function() assert_file_exists(existing_file) - eq(-ffi.C.kEEXIST, (os_open(existing_file, (bit.bor(ffi.C.kO_CREAT, ffi.C.kO_EXCL)), 0))) + eq(ffi.C.kUV_EEXIST, (os_open(existing_file, (bit.bor(ffi.C.kO_CREAT, ffi.C.kO_EXCL)), 0))) end) it('sets `rwx` permissions for O_CREAT 700', function() @@ -611,7 +602,7 @@ describe('fs function', function() it('removes the given directory and returns 0', function() lfs.mkdir('unit-test-directory/new-dir') - eq(0, (os_rmdir('unit-test-directory/new-dir', mode))) + eq(0, os_rmdir('unit-test-directory/new-dir')) eq(false, (os_isdir('unit-test-directory/new-dir'))) end) end) diff --git a/test/unit/os/shell_spec.lua b/test/unit/os/shell_spec.lua index 20cfc17950..93103e4e8c 100644 --- a/test/unit/os/shell_spec.lua +++ b/test/unit/os/shell_spec.lua @@ -11,29 +11,46 @@ 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', - './src/nvim/misc1.h' + './src/nvim/misc1.h', + './src/nvim/memory.h' ) -local ffi, eq, neq = helpers.ffi, helpers.eq, helpers.neq +local ffi, eq = helpers.ffi, helpers.eq local intern = helpers.internalize local to_cstr = helpers.to_cstr 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 = cimported.shell_build_argv( + cmd and to_cstr(cmd), + extra_args and to_cstr(extra_args)) + local argc = 0 + local ret = {} + -- Explicitly free everything, so if it is not in allocated memory it will + -- crash. + while res[argc] ~= nil do + ret[#ret + 1] = ffi.string(res[argc]) + cimported.xfree(res[argc]) + argc = argc + 1 + end + cimported.xfree(res) + return ret + end + local function os_system(cmd, input) local input_or = input and to_cstr(input) or NULL local input_len = (input ~= nil) and string.len(input) or 0 @@ -41,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 @@ -70,8 +87,48 @@ describe('shell functions', function() end) it ('returns non-zero exit code', function() - local status, output = os_system('exit 2') + local status = os_system('exit 2') eq(2, status) end) end) + + describe('shell_build_argv', function() + local saved_opts = {} + + setup(function() + saved_opts.p_sh = cimported.p_sh + saved_opts.p_shcf = cimported.p_shcf + end) + + teardown(function() + cimported.p_sh = saved_opts.p_sh + cimported.p_shcf = saved_opts.p_shcf + end) + + it('works with NULL arguments', function() + eq({'/bin/bash'}, shell_build_argv(nil, nil)) + end) + + it('works with cmd', function() + eq({'/bin/bash', '-c', 'abc def'}, shell_build_argv('abc def', nil)) + end) + + it('works with extra_args', function() + eq({'/bin/bash', 'ghi jkl'}, shell_build_argv(nil, 'ghi jkl')) + end) + + it('works with cmd and extra_args', function() + eq({'/bin/bash', 'ghi jkl', '-c', 'abc def'}, shell_build_argv('abc def', 'ghi jkl')) + end) + + it('splits and unquotes &shell and &shellcmdflag', function() + 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', + '-c', 'abc def'}, + shell_build_argv('abc def', 'ghi jkl')) + end) + end) end) diff --git a/test/unit/os/users_spec.lua b/test/unit/os/users_spec.lua index df5d2365c6..236481e9e7 100644 --- a/test/unit/os/users_spec.lua +++ b/test/unit/os/users_spec.lua @@ -1,26 +1,24 @@ local helpers = require('test.unit.helpers') local cimport = helpers.cimport -local internalize = helpers.internalize local eq = helpers.eq local ffi = helpers.ffi local lib = helpers.lib -local cstr = helpers.cstr local NULL = helpers.NULL local OK = helpers.OK local FAIL = helpers.FAIL local users = cimport('./src/nvim/os/os.h', 'unistd.h') -function garray_new() +local function garray_new() return ffi.new('garray_T[1]') end -function garray_get_len(array) +local function garray_get_len(array) return array[0].ga_len end -function garray_get_item(array, index) +local function garray_get_item(array, index) return (ffi.cast('void **', array[0].ga_data))[index] end diff --git a/test/unit/path_spec.lua b/test/unit/path_spec.lua index 239a255151..9b76834383 100644 --- a/test/unit/path_spec.lua +++ b/test/unit/path_spec.lua @@ -1,18 +1,16 @@ +local lfs = require('lfs') local helpers = require('test.unit.helpers') local cimport = helpers.cimport -local internalize = helpers.internalize local eq = helpers.eq local neq = helpers.neq local ffi = helpers.ffi -local lib = helpers.lib local cstr = helpers.cstr local to_cstr = helpers.to_cstr local NULL = helpers.NULL local OK = helpers.OK local FAIL = helpers.FAIL -require('lfs') cimport('string.h') local path = cimport('./src/nvim/path.h') @@ -23,7 +21,7 @@ local kBothFilesMissing = path.kBothFilesMissing local kOneFileMissing = path.kOneFileMissing local kEqualFileNames = path.kEqualFileNames -local len = 0 +local length = 0 local buffer = nil describe('path function', function() @@ -36,19 +34,19 @@ describe('path function', function() lfs.rmdir('unit-test-directory') end) - function path_full_dir_name(directory, buffer, len) + local function path_full_dir_name(directory, buf, len) directory = to_cstr(directory) - return path.path_full_dir_name(directory, buffer, len) + return path.path_full_dir_name(directory, buf, len) end before_each(function() -- Create empty string buffer which will contain the resulting path. - len = (string.len(lfs.currentdir())) + 22 - buffer = cstr(len, '') + length = string.len(lfs.currentdir()) + 22 + buffer = cstr(length, '') end) it('returns the absolute directory name of a given relative one', function() - local result = path_full_dir_name('..', buffer, len) + local result = path_full_dir_name('..', buffer, length) eq(OK, result) local old_dir = lfs.currentdir() lfs.chdir('..') @@ -58,23 +56,23 @@ describe('path function', function() end) it('returns the current directory name if the given string is empty', function() - eq(OK, (path_full_dir_name('', buffer, len))) + eq(OK, (path_full_dir_name('', buffer, length))) eq(lfs.currentdir(), (ffi.string(buffer))) end) it('fails if the given directory does not exist', function() - eq(FAIL, path_full_dir_name('does_not_exist', buffer, len)) + eq(FAIL, path_full_dir_name('does_not_exist', buffer, length)) end) it('works with a normal relative dir', function() - local result = path_full_dir_name('unit-test-directory', buffer, len) + local result = path_full_dir_name('unit-test-directory', buffer, length) eq(lfs.currentdir() .. '/unit-test-directory', (ffi.string(buffer))) eq(OK, result) end) end) describe('path_full_compare', function() - function path_full_compare(s1, s2, cn) + 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) @@ -117,7 +115,7 @@ describe('path function', function() end) describe('path_tail', function() - function path_tail(file) + local function path_tail(file) local res = path.path_tail((to_cstr(file))) neq(NULL, res) return ffi.string(res) @@ -133,7 +131,7 @@ describe('path function', function() end) describe('path_tail_with_sep', function() - function path_tail_with_sep(file) + local function path_tail_with_sep(file) local res = path.path_tail_with_sep((to_cstr(file))) neq(NULL, res) return ffi.string(res) @@ -165,7 +163,7 @@ describe('path function', function() -- Returns the path tail and length (out param) of the tail. -- Does not convert the tail from C-pointer to lua string for use with -- strcmp. - function invocation_path_tail(invk) + local function invocation_path_tail(invk) local plen = ffi.new('size_t[?]', 1) local ptail = path.invocation_path_tail((to_cstr(invk)), plen) neq(NULL, ptail) @@ -178,7 +176,7 @@ describe('path function', function() end -- This test mimics the intended use in C. - function compare(base, pinvk, len) + local function compare(base, pinvk, len) return eq(0, (ffi.C.strncmp((to_cstr(base)), pinvk, len))) end @@ -207,7 +205,7 @@ describe('path function', function() end) it('only accepts whitespace as a terminator for the executable name', function() - local invk, len = invocation_path_tail('exe-a+b_c[]()|#!@$%^&*') + local invk, _ = invocation_path_tail('exe-a+b_c[]()|#!@$%^&*') eq('exe-a+b_c[]()|#!@$%^&*', (ffi.string(invk))) end) @@ -215,20 +213,20 @@ describe('path function', function() local ptail = path.path_tail(to_cstr("a/b/c x y z")) neq(NULL, ptail) local tail = ffi.string(ptail) - local invk, len = invocation_path_tail("a/b/c x y z") + local invk, _ = invocation_path_tail("a/b/c x y z") eq(tail, ffi.string(invk)) end) it('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")) neq(NULL, ptail) - local invk, len = invocation_path_tail("a/b/c x y/z") + local invk, _ = invocation_path_tail("a/b/c x y/z") neq((ffi.string(ptail)), (ffi.string(invk))) end) end) describe('path_next_component', function() - function path_next_component(file) + local function path_next_component(file) local res = path.path_next_component((to_cstr(file))) neq(NULL, res) return ffi.string(res) @@ -308,11 +306,11 @@ describe('more path function', function() -- Since the tests are executed, they are called by an executable. We use -- that executable for several asserts. - absolute_executable = arg[0] + local absolute_executable = arg[0] -- Split absolute_executable into a directory and the actual file name for -- later usage. - directory, executable_name = string.match(absolute_executable, '^(.*)/(.*)$') + local directory, executable_name = string.match(absolute_executable, '^(.*)/(.*)$') -- luacheck: ignore end) teardown(function() @@ -321,27 +319,27 @@ describe('more path function', function() end) describe('vim_FullName', function() - function vim_FullName(filename, buffer, length, force) + local function vim_FullName(filename, buf, len, force) filename = to_cstr(filename) - return path.vim_FullName(filename, buffer, length, force) + return path.vim_FullName(filename, buf, len, force) end before_each(function() -- Create empty string buffer which will contain the resulting path. - len = (string.len(lfs.currentdir())) + 33 - buffer = cstr(len, '') + length = (string.len(lfs.currentdir())) + 33 + buffer = cstr(length, '') end) it('fails if given filename is NULL', function() local force_expansion = 1 - local result = path.vim_FullName(NULL, buffer, len, force_expansion) + local result = path.vim_FullName(NULL, buffer, length, force_expansion) eq(FAIL, result) end) it('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, len, force_expansion) + local result = vim_FullName(filename, buffer, length, force_expansion) eq(filename, (ffi.string(buffer))) eq(OK, result) end) @@ -349,14 +347,14 @@ describe('more path function', function() it('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, len, force_expansion) + local result = vim_FullName(filename, buffer, length, force_expansion) eq(filename, (ffi.string(buffer))) eq(FAIL, result) end) it('concatenates given filename if it does not contain a slash', function() local force_expansion = 1 - local result = vim_FullName('test.file', buffer, len, force_expansion) + local result = vim_FullName('test.file', buffer, length, force_expansion) local expected = lfs.currentdir() .. '/test.file' eq(expected, (ffi.string(buffer))) eq(OK, result) @@ -364,7 +362,7 @@ describe('more path function', function() it('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, len, force_expansion) + local result = vim_FullName('..', buffer, length, force_expansion) local expected = lfs.currentdir() .. '/..' eq(expected, (ffi.string(buffer))) eq(OK, result) @@ -374,7 +372,7 @@ describe('more path function', function() -- the unit tests? Which other directory would be better? it('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, len, force_expansion) + local result = vim_FullName('../test.file', buffer, length, force_expansion) local old_dir = lfs.currentdir() lfs.chdir('..') local expected = lfs.currentdir() .. '/test.file' @@ -386,7 +384,7 @@ describe('more path function', function() it('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, len, force_expansion) + local result = vim_FullName(absolute_path, buffer, length, force_expansion) eq(absolute_path, (ffi.string(buffer))) eq(OK, result) end) @@ -394,14 +392,14 @@ describe('more path function', function() it('fails and uses filename when the path is relative to HOME', function() local force_expansion = 1 local absolute_path = '~/home.file' - local result = vim_FullName(absolute_path, buffer, len, force_expansion) + local result = vim_FullName(absolute_path, buffer, length, force_expansion) eq(absolute_path, (ffi.string(buffer))) eq(FAIL, result) end) it('works with some "normal" relative path with directories', function() local force_expansion = 1 - local result = vim_FullName('unit-test-directory/test.file', buffer, len, force_expansion) + local result = vim_FullName('unit-test-directory/test.file', buffer, length, force_expansion) eq(OK, result) eq(lfs.currentdir() .. '/unit-test-directory/test.file', (ffi.string(buffer))) end) @@ -411,7 +409,7 @@ describe('more path function', function() 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, len, force_expansion) + 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))) eq(OK, result) @@ -420,15 +418,15 @@ describe('more path function', function() it('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, len, force_expansion) + local result = path.vim_FullName(filename, buffer, length, force_expansion) eq('/tmp', ffi.string(buffer)) eq(OK, result) end) end) describe('path_fix_case', function() - function fix_case(file) - c_file = to_cstr(file) + local function fix_case(file) + local c_file = to_cstr(file) path.path_fix_case(c_file) return ffi.string(c_file) end @@ -493,7 +491,7 @@ describe('more path function', function() end) describe('path_is_absolute_path', function() - function path_is_absolute_path(filename) + local function path_is_absolute_path(filename) filename = to_cstr(filename) return path.path_is_absolute_path(filename) end diff --git a/test/unit/preprocess.lua b/test/unit/preprocess.lua index d4c2e088a4..e5c838b13b 100644 --- a/test/unit/preprocess.lua +++ b/test/unit/preprocess.lua @@ -169,8 +169,8 @@ local type_to_class = { -- find the best cc. If os.exec causes problems on windows (like popping up -- a console window) we might consider using something like this: -- http://scite-ru.googlecode.com/svn/trunk/pack/tools/LuaLib/shell.html#exec -local function find_best_cc(ccs) - for _, meta in pairs(ccs) do +local function find_best_cc(compilers) + for _, meta in pairs(compilers) do local version = io.popen(tostring(meta.path) .. " -v 2>&1") version:close() if version then diff --git a/test/unit/profile_spec.lua b/test/unit/profile_spec.lua index 2b006a0768..852475fe2c 100644 --- a/test/unit/profile_spec.lua +++ b/test/unit/profile_spec.lua @@ -36,20 +36,20 @@ local function cmp_assert(v1, v2, op, opstr) assert.is_true(res) end -local function lt(v1, v2) - cmp_assert(v1, v2, function(v1, v2) return v1 < v2 end, "<") +local function lt(a, b) -- luacheck: ignore + cmp_assert(a, b, function(x, y) return x < y end, "<") end -local function lte(v1, v2) - cmp_assert(v1, v2, function(v1, v2) return v1 <= v2 end, "<=") +local function lte(a, b) -- luacheck: ignore + cmp_assert(a, b, function(x, y) return x <= y end, "<=") end -local function gt(v1, v2) - cmp_assert(v1, v2, function(v1, v2) return v1 > v2 end, ">") +local function gt(a, b) -- luacheck: ignore + cmp_assert(a, b, function(x, y) return x > y end, ">") end -local function gte(v1, v2) - cmp_assert(v1, v2, function(v1, v2) return v1 >= v2 end, ">=") +local function gte(a, b) + cmp_assert(a, b, function(x, y) return x >= y end, ">=") end -- missing functions: @@ -70,7 +70,7 @@ describe('profiling related functions', function() local function profile_equal(t1, t2) return prof.profile_equal(t1, t2) end local function profile_msg(t) return ffi.string(prof.profile_msg(t)) end - local function toseconds(t) + local function toseconds(t) -- luacheck: ignore local str = trim(profile_msg(t)) local spl = split(str, ".") local s, us = spl[1], spl[2] @@ -122,7 +122,7 @@ describe('profiling related functions', function() local divided = profile_divide(start, divisor) local res = divided - for i = 1, divisor - 1 do + for _ = 1, divisor - 1 do res = profile_add(res, divided) end @@ -143,7 +143,7 @@ describe('profiling related functions', function() describe('profile_start', function() it('increases', function() local last = profile_start() - for i=1,100 do + for _ = 1, 100 do local curr = profile_start() gte(curr, last) last = curr @@ -157,7 +157,7 @@ describe('profiling related functions', function() end) it('outer elapsed >= inner elapsed', function() - for i = 1, 100 do + for _ = 1, 100 do local start_outer = profile_start() local start_inner = profile_start() local elapsed_inner = profile_end(start_inner) @@ -238,7 +238,7 @@ describe('profiling related functions', function() -- t2 >= t1 => profile_cmp(t1, t2) >= 0 assert.is_true(cmp >= 0) - local cmp = profile_cmp(profile_sub(start3, start1), profile_sub(start2, start1)) + cmp = profile_cmp(profile_sub(start3, start1), profile_sub(start2, start1)) -- t2 <= t1 => profile_cmp(t1, t2) <= 0 assert.is_true(cmp <= 0) end) diff --git a/test/unit/set.lua b/test/unit/set.lua index bfb6b8c41c..4e66546f32 100644 --- a/test/unit/set.lua +++ b/test/unit/set.lua @@ -35,7 +35,7 @@ end -- adds the argument table to this Set function Set:union_table(t) - for k, v in pairs(t) do + for _, v in pairs(t) do self:add(v) end end diff --git a/test/unit/strings_spec.lua b/test/unit/strings_spec.lua new file mode 100644 index 0000000000..e935d2af6a --- /dev/null +++ b/test/unit/strings_spec.lua @@ -0,0 +1,68 @@ +local helpers = require("test.unit.helpers") + +local cimport = helpers.cimport +local eq = helpers.eq +local ffi = helpers.ffi +local to_cstr = helpers.to_cstr + +local strings = cimport('stdlib.h', './src/nvim/strings.h', + './src/nvim/memory.h') + +describe('vim_strnsave_unquoted()', function() + local vim_strnsave_unquoted = function(s, len) + local res = strings.vim_strnsave_unquoted(to_cstr(s), len or #s) + local ret = ffi.string(res) + -- Explicitly free memory so we are sure it is allocated: if it was not it + -- will crash. + strings.xfree(res) + return ret + end + + it('copies unquoted strings as-is', function() + eq('-c', vim_strnsave_unquoted('-c')) + eq('', vim_strnsave_unquoted('')) + end) + + it('respects length argument', function() + eq('', vim_strnsave_unquoted('-c', 0)) + eq('-', vim_strnsave_unquoted('-c', 1)) + eq('-', vim_strnsave_unquoted('"-c', 2)) + end) + + it('unquotes fully quoted word', function() + eq('/bin/sh', vim_strnsave_unquoted('"/bin/sh"')) + end) + + it('unquotes partially quoted word', function() + eq('/Program Files/sh', vim_strnsave_unquoted('/Program" "Files/sh')) + end) + + it('removes ""', function() + eq('/Program Files/sh', vim_strnsave_unquoted('/""Program" "Files/sh')) + end) + + it('performs unescaping of "', function() + eq('/"Program Files"/sh', vim_strnsave_unquoted('/"\\""Program Files"\\""/sh')) + end) + + it('performs unescaping of \\', function() + eq('/\\Program Files\\foo/sh', vim_strnsave_unquoted('/"\\\\"Program Files"\\\\foo"/sh')) + end) + + it('strips quote when there is no pair to it', function() + eq('/Program Files/sh', vim_strnsave_unquoted('/Program" Files/sh')) + eq('', vim_strnsave_unquoted('"')) + end) + + it('allows string to end with one backslash unescaped', function() + eq('/Program Files/sh\\', vim_strnsave_unquoted('/Program" Files/sh\\')) + end) + + it('does not perform unescaping out of quotes', function() + eq('/Program\\ Files/sh\\', vim_strnsave_unquoted('/Program\\ Files/sh\\')) + end) + + it('does not unescape \\n', function() + eq('/Program\\nFiles/sh', vim_strnsave_unquoted('/Program"\\n"Files/sh')) + end) +end) diff --git a/test/unit/tempfile_spec.lua b/test/unit/tempfile_spec.lua index e558ff04c8..7975d11aed 100644 --- a/test/unit/tempfile_spec.lua +++ b/test/unit/tempfile_spec.lua @@ -2,9 +2,12 @@ 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() + before_each(function() + tempfile.vim_deltempdir() + end) after_each(function() tempfile.vim_deltempdir() end) |