diff options
author | Josh Rahm <joshuarahm@gmail.com> | 2023-11-29 21:52:58 +0000 |
---|---|---|
committer | Josh Rahm <joshuarahm@gmail.com> | 2023-11-29 21:52:58 +0000 |
commit | 931bffbda3668ddc609fc1da8f9eb576b170aa52 (patch) | |
tree | d8c1843a95da5ea0bb4acc09f7e37843d9995c86 /test/unit | |
parent | 142d9041391780ac15b89886a54015fdc5c73995 (diff) | |
parent | 4a8bf24ac690004aedf5540fa440e788459e5e34 (diff) | |
download | rneovim-userreg.tar.gz rneovim-userreg.tar.bz2 rneovim-userreg.zip |
Merge remote-tracking branch 'upstream/master' into userreguserreg
Diffstat (limited to 'test/unit')
27 files changed, 1486 insertions, 1244 deletions
diff --git a/test/unit/buffer_spec.lua b/test/unit/buffer_spec.lua index a54ea8c656..6e08a09295 100644 --- a/test/unit/buffer_spec.lua +++ b/test/unit/buffer_spec.lua @@ -1,15 +1,11 @@ - local helpers = require("test.unit.helpers")(after_each) local itp = helpers.gen_itp(it) local to_cstr = helpers.to_cstr -local get_str = helpers.ffi.string local eq = helpers.eq local NULL = helpers.NULL -local globals = helpers.cimport("./src/nvim/globals.h") local buffer = helpers.cimport("./src/nvim/buffer.h") -local stl = helpers.cimport("./src/nvim/statusline.h") describe('buffer functions', function() @@ -210,276 +206,4 @@ describe('buffer functions', function() close_buffer(NULL, buf2, buffer.DOBUF_WIPE, 0, 0) end) end) - - describe('build_stl_str_hl', function() - local buffer_byte_size = 100 - local STL_INITIAL_ITEMS = 20 - local output_buffer = '' - - -- This function builds the statusline - -- - -- @param arg Optional arguments are: - -- .pat The statusline format string - -- .fillchar The fill character used in the statusline - -- .maximum_cell_count The number of cells available in the statusline - local function build_stl_str_hl(arg) - output_buffer = to_cstr(string.rep(" ", buffer_byte_size)) - - local pat = arg.pat or '' - local fillchar = arg.fillchar or (' '):byte() - local maximum_cell_count = arg.maximum_cell_count or buffer_byte_size - - return stl.build_stl_str_hl(globals.curwin, - output_buffer, - buffer_byte_size, - to_cstr(pat), - NULL, - 0, - fillchar, - maximum_cell_count, - NULL, - NULL, - NULL) - end - - -- Use this function to simplify testing the comparison between - -- the format string and the resulting statusline. - -- - -- @param description The description of what the test should be doing - -- @param statusline_cell_count The number of cells available in the statusline - -- @param input_stl The format string for the statusline - -- @param expected_stl The expected result string for the statusline - -- - -- @param arg Options can be placed in an optional dictionary as the last parameter - -- .expected_cell_count The expected number of cells build_stl_str_hl will return - -- .expected_byte_length The expected byte length of the string (defaults to byte length of expected_stl) - -- .file_name The name of the file to be tested (useful in %f type tests) - -- .fillchar The character that will be used to fill any 'extra' space in the stl - local function statusline_test (description, - statusline_cell_count, - input_stl, - expected_stl, - arg) - - -- arg is the optional parameter - -- so we either fill in option with arg or an empty dictionary - local option = arg or {} - - local fillchar = option.fillchar or (' '):byte() - local expected_cell_count = option.expected_cell_count or statusline_cell_count - local expected_byte_length = option.expected_byte_length or #expected_stl - - itp(description, function() - if option.file_name then - buffer.setfname(globals.curbuf, to_cstr(option.file_name), NULL, 1) - else - buffer.setfname(globals.curbuf, nil, NULL, 1) - end - - local result_cell_count = build_stl_str_hl{pat=input_stl, - maximum_cell_count=statusline_cell_count, - fillchar=fillchar} - - eq(expected_stl, get_str(output_buffer, expected_byte_length)) - eq(expected_cell_count, result_cell_count) - end) - end - - -- expression testing - statusline_test('Should expand expression', 2, - '%!expand(20+1)', '21') - statusline_test('Should expand broken expression to itself', 11, - '%!expand(20+1', 'expand(20+1') - - -- file name testing - statusline_test('should print no file name', 10, - '%f', '[No Name]', - {expected_cell_count=9}) - statusline_test('should print the relative file name', 30, - '%f', 'test/unit/buffer_spec.lua', - {file_name='test/unit/buffer_spec.lua', expected_cell_count=25}) - statusline_test('should print the full file name', 40, - '%F', '/test/unit/buffer_spec.lua', - {file_name='/test/unit/buffer_spec.lua', expected_cell_count=26}) - - -- fillchar testing - statusline_test('should handle `!` as a fillchar', 10, - 'abcde%=', 'abcde!!!!!', - {fillchar=('!'):byte()}) - statusline_test('should handle `~` as a fillchar', 10, - '%=abcde', '~~~~~abcde', - {fillchar=('~'):byte()}) - statusline_test('should put fillchar `!` in between text', 10, - 'abc%=def', 'abc!!!!def', - {fillchar=('!'):byte()}) - statusline_test('should put fillchar `~` in between text', 10, - 'abc%=def', 'abc~~~~def', - {fillchar=('~'):byte()}) - statusline_test('should put fillchar `━` in between text', 10, - 'abc%=def', 'abc━━━━def', - {fillchar=0x2501}) - statusline_test('should handle zero-fillchar as a space', 10, - 'abcde%=', 'abcde ', - {fillchar=0}) - statusline_test('should print the tail file name', 80, - '%t', 'buffer_spec.lua', - {file_name='test/unit/buffer_spec.lua', expected_cell_count=15}) - - -- standard text testing - statusline_test('should copy plain text', 80, - 'this is a test', 'this is a test', - {expected_cell_count=14}) - - -- line number testing - statusline_test('should print the buffer number', 80, - '%n', '1', - {expected_cell_count=1}) - statusline_test('should print the current line number in the buffer', 80, - '%l', '0', - {expected_cell_count=1}) - statusline_test('should print the number of lines in the buffer', 80, - '%L', '1', - {expected_cell_count=1}) - - -- truncation testing - statusline_test('should truncate when standard text pattern is too long', 10, - '0123456789abcde', '<6789abcde') - statusline_test('should truncate when using =', 10, - 'abcdef%=ghijkl', 'abcdef<jkl') - statusline_test('should truncate centered text when using ==', 10, - 'abcde%=gone%=fghij', 'abcde<ghij') - statusline_test('should respect the `<` marker', 10, - 'abc%<defghijkl', 'abc<ghijkl') - statusline_test('should truncate at `<` with one `=`, test 1', 10, - 'abc%<def%=ghijklmno', 'abc<jklmno') - statusline_test('should truncate at `<` with one `=`, test 2', 10, - 'abcdef%=ghijkl%<mno', 'abcdefghi>') - statusline_test('should truncate at `<` with one `=`, test 3', 10, - 'abc%<def%=ghijklmno', 'abc<jklmno') - statusline_test('should truncate at `<` with one `=`, test 4', 10, - 'abc%<def%=ghij', 'abcdefghij') - statusline_test('should truncate at `<` with one `=`, test 4', 10, - 'abc%<def%=ghijk', 'abc<fghijk') - - statusline_test('should truncate at `<` with many `=`, test 4', 10, - 'ab%<cdef%=g%=h%=ijk', 'ab<efghijk') - - statusline_test('should truncate at the first `<`', 10, - 'abc%<def%<ghijklm', 'abc<hijklm') - - statusline_test('should ignore trailing %', 3, 'abc%', 'abc') - - -- alignment testing with fillchar - local function statusline_test_align (description, - statusline_cell_count, - input_stl, - expected_stl, - arg) - arg = arg or {} - statusline_test(description .. ' without fillchar', - statusline_cell_count, input_stl, expected_stl:gsub('%~', ' '), arg) - arg.fillchar = ('!'):byte() - statusline_test(description .. ' with fillchar `!`', - statusline_cell_count, input_stl, expected_stl:gsub('%~', '!'), arg) - arg.fillchar = 0x2501 - statusline_test(description .. ' with fillchar `━`', - statusline_cell_count, input_stl, expected_stl:gsub('%~', '━'), arg) - end - - statusline_test_align('should right align when using =', 20, - 'neo%=vim', 'neo~~~~~~~~~~~~~~vim') - statusline_test_align('should, when possible, center text when using %=text%=', 20, - 'abc%=neovim%=def', 'abc~~~~neovim~~~~def') - statusline_test_align('should handle uneven spacing in the buffer when using %=text%=', 20, - 'abc%=neo_vim%=def', 'abc~~~neo_vim~~~~def') - statusline_test_align('should have equal spaces even with non-equal sides when using =', 20, - 'foobar%=test%=baz', 'foobar~~~test~~~~baz') - statusline_test_align('should have equal spaces even with longer right side when using =', 20, - 'a%=test%=longtext', 'a~~~test~~~~longtext') - statusline_test_align('should handle an empty left side when using ==', 20, - '%=test%=baz', '~~~~~~test~~~~~~~baz') - statusline_test_align('should handle an empty right side when using ==', 20, - 'foobar%=test%=', 'foobar~~~~~test~~~~~') - statusline_test_align('should handle consecutive empty ==', 20, - '%=%=test%=', '~~~~~~~~~~test~~~~~~') - statusline_test_align('should handle an = alone', 20, - '%=', '~~~~~~~~~~~~~~~~~~~~') - statusline_test_align('should right align text when it is alone with =', 20, - '%=foo', '~~~~~~~~~~~~~~~~~foo') - statusline_test_align('should left align text when it is alone with =', 20, - 'foo%=', 'foo~~~~~~~~~~~~~~~~~') - - statusline_test_align('should approximately center text when using %=text%=', 21, - 'abc%=neovim%=def', 'abc~~~~neovim~~~~~def') - statusline_test_align('should completely fill the buffer when using %=text%=', 21, - 'abc%=neo_vim%=def', 'abc~~~~neo_vim~~~~def') - statusline_test_align('should have equal spacing even with non-equal sides when using =', 21, - 'foobar%=test%=baz', 'foobar~~~~test~~~~baz') - statusline_test_align('should have equal spacing even with longer right side when using =', 21, - 'a%=test%=longtext', 'a~~~~test~~~~longtext') - statusline_test_align('should handle an empty left side when using ==', 21, - '%=test%=baz', '~~~~~~~test~~~~~~~baz') - statusline_test_align('should handle an empty right side when using ==', 21, - 'foobar%=test%=', 'foobar~~~~~test~~~~~~') - - statusline_test_align('should quadrant the text when using 3 %=', 40, - 'abcd%=n%=eovim%=ef', 'abcd~~~~~~~~~n~~~~~~~~~eovim~~~~~~~~~~ef') - statusline_test_align('should work well with %t', 40, - '%t%=right_aligned', 'buffer_spec.lua~~~~~~~~~~~~right_aligned', - {file_name='test/unit/buffer_spec.lua'}) - statusline_test_align('should work well with %t and regular text', 40, - 'l%=m_l %t m_r%=r', 'l~~~~~~~m_l buffer_spec.lua m_r~~~~~~~~r', - {file_name='test/unit/buffer_spec.lua'}) - statusline_test_align('should work well with %=, %t, %L, and %l', 40, - '%t %= %L %= %l', 'buffer_spec.lua ~~~~~~~~~ 1 ~~~~~~~~~~ 0', - {file_name='test/unit/buffer_spec.lua'}) - - statusline_test_align('should quadrant the text when using 3 %=', 41, - 'abcd%=n%=eovim%=ef', 'abcd~~~~~~~~~n~~~~~~~~~eovim~~~~~~~~~~~ef') - statusline_test_align('should work well with %t', 41, - '%t%=right_aligned', 'buffer_spec.lua~~~~~~~~~~~~~right_aligned', - {file_name='test/unit/buffer_spec.lua'}) - statusline_test_align('should work well with %t and regular text', 41, - 'l%=m_l %t m_r%=r', 'l~~~~~~~~m_l buffer_spec.lua m_r~~~~~~~~r', - {file_name='test/unit/buffer_spec.lua'}) - statusline_test_align('should work well with %=, %t, %L, and %l', 41, - '%t %= %L %= %l', 'buffer_spec.lua ~~~~~~~~~~ 1 ~~~~~~~~~~ 0', - {file_name='test/unit/buffer_spec.lua'}) - - statusline_test_align('should work with 10 %=', 50, - 'aaaa%=b%=c%=d%=e%=fg%=hi%=jk%=lmnop%=qrstuv%=wxyz', - 'aaaa~~b~~c~~d~~e~~fg~~hi~~jk~~lmnop~~qrstuv~~~wxyz') - - -- stl item testing - local tabline = '' - for i= 1, 1000 do - tabline = tabline .. (i % 2 == 0 and '%#TabLineSel#' or '%#TabLineFill#') .. tostring(i % 2) - end - statusline_test('should handle a large amount of any items', 20, - tabline, - '<1010101010101010101') -- Should not show any error - statusline_test('should handle a larger amount of = than stl initial item', 20, - ('%='):rep(STL_INITIAL_ITEMS * 5), - ' ') -- Should not show any error - statusline_test('should handle many extra characters', 20, - 'a' .. ('a'):rep(STL_INITIAL_ITEMS * 5), - '<aaaaaaaaaaaaaaaaaaa') -- Does not show any error - statusline_test('should handle many extra characters and flags', 20, - 'a' .. ('%=a'):rep(STL_INITIAL_ITEMS * 2), - 'a<aaaaaaaaaaaaaaaaaa') -- Should not show any error - - - -- multi-byte testing - statusline_test('should handle multibyte characters', 10, - 'Ĉ%=x', 'Ĉ x') - statusline_test('should handle multibyte characters and different fillchars', 10, - 'Ą%=mid%=end', 'Ą@mid@@end', - {fillchar=('@'):byte()}) - - -- escaping % testing - statusline_test('should handle escape of %', 4, 'abc%%', 'abc%') - statusline_test('case where escaped % does not fit', 3, 'abc%%abcabc', '<bc') - statusline_test('escaped % is first', 1, '%%', '%') - - end) end) diff --git a/test/unit/charset/vim_str2nr_spec.lua b/test/unit/charset/vim_str2nr_spec.lua index caf330c378..fc40fa39d4 100644 --- a/test/unit/charset/vim_str2nr_spec.lua +++ b/test/unit/charset/vim_str2nr_spec.lua @@ -63,7 +63,7 @@ local function test_vim_str2nr(s, what, exp, maxlen, strict) cv[k] = args[k] end end - lib.vim_str2nr(s, cv.pre, cv.len, what, cv.num, cv.unum, maxlen, strict) + lib.vim_str2nr(s, cv.pre, cv.len, what, cv.num, cv.unum, maxlen, strict, nil) for cck, ccv in pairs(cv) do if exp[cck] ~= tonumber(ccv[0]) then error(('Failed check (%s = %d) in test (s=%s, w=%u, m=%d, strict=%s): %d'):format( diff --git a/test/unit/eval/helpers.lua b/test/unit/eval/helpers.lua index b600f01ab2..883f01bd84 100644 --- a/test/unit/eval/helpers.lua +++ b/test/unit/eval/helpers.lua @@ -349,7 +349,7 @@ local special_vals = nil lua2typvalt = function(l, processed) if not special_vals then special_vals = { - [null_string] = {'VAR_STRING', {v_string=ffi.cast('char_u*', nil)}}, + [null_string] = {'VAR_STRING', {v_string=ffi.cast('char*', nil)}}, [null_list] = {'VAR_LIST', {v_list=ffi.cast('list_T*', nil)}}, [null_dict] = {'VAR_DICT', {v_dict=ffi.cast('dict_T*', nil)}}, [nil_value] = {'VAR_SPECIAL', {v_special=eval.kSpecialVarNull}}, @@ -512,7 +512,8 @@ end local function eval0(expr) local tv = ffi.gc(ffi.new('typval_T', {v_type=eval.VAR_UNKNOWN}), eval.tv_clear) - if eval.eval0(to_cstr(expr), tv, nil, true) == 0 then + local evalarg = ffi.new('evalarg_T', {eval_flags = eval.EVAL_EVALUATE}) + if eval.eval0(to_cstr(expr), tv, nil, evalarg) == 0 then return nil else return tv diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 128625374e..34a1299725 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -43,7 +43,7 @@ local dict_watchers = eval_helpers.dict_watchers local lib = cimport('./src/nvim/eval/typval.h', './src/nvim/memory.h', './src/nvim/mbyte.h', './src/nvim/garray.h', - './src/nvim/eval.h', './src/nvim/vim.h', + './src/nvim/eval.h', './src/nvim/vim_defs.h', './src/nvim/globals.h') local function vimconv_alloc() @@ -101,12 +101,13 @@ local function check_emsg(f, msg) saved_last_msg_hist = nil end local ret = {f()} + local last_msg = lib.last_msg_hist ~= nil and ffi.string(lib.last_msg_hist.msg) or nil if msg ~= nil then - eq(msg, ffi.string(lib.last_msg_hist.msg)) + eq(msg, last_msg) neq(saved_last_msg_hist, lib.last_msg_hist) else if saved_last_msg_hist ~= lib.last_msg_hist then - eq(nil, ffi.string(lib.last_msg_hist.msg)) + eq(nil, last_msg) else eq(saved_last_msg_hist, lib.last_msg_hist) end @@ -1429,15 +1430,16 @@ describe('typval.c', function() end describe('str()', function() itp('returns correct string', function() - local l = list(int(1), int(2), int(3), int(4), int(5)) + local l = list(int(1), 2.5, int(3), int(4), int(5)) alloc_log:clear() eq('1', tv_list_find_str(l, -5)) + eq('2.5', tv_list_find_str(l, 1)) eq('5', tv_list_find_str(l, 4)) eq('3', tv_list_find_str(l, 2)) eq('3', tv_list_find_str(l, -3)) - alloc_log:check({}) + alloc_log:check({a.freed(alloc_log.null), a.freed(alloc_log.null)}) end) itp('returns string when used with VAR_STRING items', function() local l = list('1', '2', '3', '4', '5') @@ -1461,18 +1463,16 @@ describe('typval.c', function() itp('fails with error message when index is out of range', function() local l = list(int(1), int(2), int(3), int(4), int(5)) - eq(nil, tv_list_find_str(l, -6, 'E684: list index out of range: -6')) - eq(nil, tv_list_find_str(l, 5, 'E684: list index out of range: 5')) + eq(nil, tv_list_find_str(l, -6, 'E684: List index out of range: -6')) + eq(nil, tv_list_find_str(l, 5, 'E684: List index out of range: 5')) end) itp('fails with error message on invalid types', function() - local l = list(1, empty_list, {}) + local l = list(empty_list, {}) - eq('', tv_list_find_str(l, 0, 'E806: using Float as a String')) - eq('', tv_list_find_str(l, 1, 'E730: using List as a String')) - eq('', tv_list_find_str(l, 2, 'E731: using Dictionary as a String')) - eq('', tv_list_find_str(l, -1, 'E731: using Dictionary as a String')) - eq('', tv_list_find_str(l, -2, 'E730: using List as a String')) - eq('', tv_list_find_str(l, -3, 'E806: using Float as a String')) + eq('', tv_list_find_str(l, 0, 'E730: Using a List as a String')) + eq('', tv_list_find_str(l, 1, 'E731: Using a Dictionary as a String')) + eq('', tv_list_find_str(l, -1, 'E731: Using a Dictionary as a String')) + eq('', tv_list_find_str(l, -2, 'E730: Using a List as a String')) end) end) end) @@ -1653,7 +1653,7 @@ describe('typval.c', function() eq(OK, lib.tv_dict_add(d, di)) alloc_log:check({}) eq(FAIL, check_emsg(function() return lib.tv_dict_add(d, di) end, - 'E685: Internal error: hash_add()')) + 'E685: Internal error: hash_add(): duplicate key ""')) alloc_log:clear() lib.tv_dict_item_remove(d, di) alloc_log:check({ @@ -1677,7 +1677,7 @@ describe('typval.c', function() eq(nil, lib.tv_dict_find(nil, 'test', -1)) eq(nil, lib.tv_dict_find(nil, nil, 0)) end) - itp('works with NULL key', function() + itp('works with empty key', function() local lua_d = { ['']=0, t=1, @@ -1692,7 +1692,6 @@ describe('typval.c', function() alloc_log:check({}) local dis = dict_items(d) eq({0, '', dis['']}, {tv_dict_find(d, '', 0)}) - eq({0, '', dis['']}, {tv_dict_find(d, nil, 0)}) end) itp('works with len properly', function() local lua_d = { @@ -1746,7 +1745,7 @@ describe('typval.c', function() itp('works', function() local d = ffi.gc(dict({test={}}), nil) eq('', ffi.string(check_emsg(function() return lib.tv_dict_get_string(d, 'test', false) end, - 'E731: using Dictionary as a String'))) + 'E731: Using a Dictionary as a String'))) d = ffi.gc(dict({tes=int(42), t=44, te='43', xx=int(45)}), nil) alloc_log:clear() local dis = dict_items(d) @@ -1766,18 +1765,24 @@ describe('typval.c', function() neq(s42, s43) eq(s43, dis.te.di_tv.vval.v_string) alloc_log:check({}) - eq('', ffi.string(check_emsg(function() return lib.tv_dict_get_string(d, 't', false) end, - 'E806: using Float as a String'))) + local s44 = check_emsg(function() return lib.tv_dict_get_string(d, 't', false) end, + nil) + eq('44.0', ffi.string(s44)) + alloc_log:check({a.freed(alloc_log.null), a.freed(alloc_log.null)}) end) itp('allocates a string copy when requested', function() - local function tv_dict_get_string_alloc(d, key, emsg) + local function tv_dict_get_string_alloc(d, key, emsg, is_float) alloc_log:clear() local ret = check_emsg(function() return lib.tv_dict_get_string(d, key, true) end, emsg) local s_ret = (ret ~= nil) and ffi.string(ret) or nil if not emsg then if s_ret then - alloc_log:check({a.str(ret, s_ret)}) + if is_float then + alloc_log:check({a.freed(alloc_log.null), a.freed(alloc_log.null), a.str(ret, s_ret)}) + else + alloc_log:check({a.str(ret, s_ret)}) + end else alloc_log:check({}) end @@ -1786,25 +1791,29 @@ describe('typval.c', function() return s_ret end local d = ffi.gc(dict({test={}}), nil) - eq('', tv_dict_get_string_alloc(d, 'test', 'E731: using Dictionary as a String')) + eq('', tv_dict_get_string_alloc(d, 'test', 'E731: Using a Dictionary as a String')) d = ffi.gc(dict({tes=int(42), t=44, te='43', xx=int(45)}), nil) alloc_log:clear() eq(nil, tv_dict_get_string_alloc(d, 'test')) eq('42', tv_dict_get_string_alloc(d, 'tes')) eq('45', tv_dict_get_string_alloc(d, 'xx')) eq('43', tv_dict_get_string_alloc(d, 'te')) - eq('', tv_dict_get_string_alloc(d, 't', 'E806: using Float as a String')) + eq('44.0', tv_dict_get_string_alloc(d, 't', nil, true)) end) end) describe('get_string_buf()', function() - local function tv_dict_get_string_buf(d, key, buf, emsg) + local function tv_dict_get_string_buf(d, key, is_float, buf, emsg) buf = buf or ffi.gc(lib.xmalloc(lib.NUMBUFLEN), lib.xfree) alloc_log:clear() local ret = check_emsg(function() return lib.tv_dict_get_string_buf(d, key, buf) end, emsg) local s_ret = (ret ~= nil) and ffi.string(ret) or nil if not emsg then - alloc_log:check({}) + if is_float then + alloc_log:check({a.freed(alloc_log.null), a.freed(alloc_log.null)}) + else + alloc_log:check({}) + end end return s_ret, ret, buf end @@ -1828,16 +1837,16 @@ describe('typval.c', function() s, r, b = tv_dict_get_string_buf(d, 'test') neq(r, b) eq('tset', s) - s, r, b = tv_dict_get_string_buf(d, 't', nil, 'E806: using Float as a String') - neq(r, b) - eq('', s) + s, r, b = tv_dict_get_string_buf(d, 't', true) + eq(r, b) + eq('1.0', s) s, r, b = tv_dict_get_string_buf(d, 'te') eq(r, b) eq('2', s) end) end) describe('get_string_buf_chk()', function() - local function tv_dict_get_string_buf_chk(d, key, len, buf, def, emsg) + local function tv_dict_get_string_buf_chk(d, key, is_float, len, buf, def, emsg) buf = buf or ffi.gc(lib.xmalloc(lib.NUMBUFLEN), lib.xfree) def = def or ffi.gc(lib.xstrdup('DEFAULT'), lib.xfree) len = len or #key @@ -1846,7 +1855,11 @@ describe('typval.c', function() emsg) local s_ret = (ret ~= nil) and ffi.string(ret) or nil if not emsg then - alloc_log:check({}) + if is_float then + alloc_log:check({a.freed(alloc_log.null), a.freed(alloc_log.null)}) + else + alloc_log:check({}) + end end return s_ret, ret, buf, def end @@ -1871,10 +1884,10 @@ describe('typval.c', function() neq(r, b) neq(r, def) eq('tset', s) - s, r, b, def = tv_dict_get_string_buf_chk(d, 'test', 1, nil, nil, 'E806: using Float as a String') - neq(r, b) + s, r, b, def = tv_dict_get_string_buf_chk(d, 'test', true, 1) + eq(r, b) neq(r, def) - eq(nil, s) + eq('1.0', s) s, r, b, def = tv_dict_get_string_buf_chk(d, 'te') eq(r, b) neq(r, def) @@ -1911,8 +1924,6 @@ describe('typval.c', function() local d = dict(lua_d) eq(lua_d, dct2tbl(d)) eq({{type='fref', fref='tr'}, true}, - {tv_dict_get_callback(d, nil, 0)}) - eq({{type='fref', fref='tr'}, true}, {tv_dict_get_callback(d, '', -1)}) eq({{type='none'}, true}, {tv_dict_get_callback(d, 'x', -1)}) @@ -1950,7 +1961,7 @@ describe('typval.c', function() alloc_log:check({}) eq({test=10, ['t-est']=int(42)}, dct2tbl(d)) eq(FAIL, check_emsg(function() return lib.tv_dict_add(d, di) end, - 'E685: Internal error: hash_add()')) + 'E685: Internal error: hash_add(): duplicate key "t-est"')) end) end) describe('list()', function() @@ -1967,7 +1978,7 @@ describe('typval.c', function() eq({test=10, tes={1, 2, 3}}, dct2tbl(d)) eq(2, l.lv_refcount) eq(FAIL, check_emsg(function() return lib.tv_dict_add_list(d, 'testt', 3, l) end, - 'E685: Internal error: hash_add()')) + 'E685: Internal error: hash_add(): duplicate key "tes"')) eq(2, l.lv_refcount) alloc_log:clear() lib.emsg_skip = lib.emsg_skip + 1 @@ -1993,7 +2004,7 @@ describe('typval.c', function() eq({test=10, tes={foo=42}}, dct2tbl(d)) eq(2, d2.dv_refcount) eq(FAIL, check_emsg(function() return lib.tv_dict_add_dict(d, 'testt', 3, d2) end, - 'E685: Internal error: hash_add()')) + 'E685: Internal error: hash_add(): duplicate key "tes"')) eq(2, d2.dv_refcount) alloc_log:clear() lib.emsg_skip = lib.emsg_skip + 1 @@ -2015,7 +2026,7 @@ describe('typval.c', function() alloc_log:check({a.di(dis.tes, 'tes')}) eq({test=10, tes=int(2)}, dct2tbl(d)) eq(FAIL, check_emsg(function() return lib.tv_dict_add_nr(d, 'testt', 3, 2) end, - 'E685: Internal error: hash_add()')) + 'E685: Internal error: hash_add(): duplicate key "tes"')) alloc_log:clear() lib.emsg_skip = lib.emsg_skip + 1 eq(FAIL, check_emsg(function() return lib.tv_dict_add_nr(d, 'testt', 3, 2) end, @@ -2035,7 +2046,7 @@ describe('typval.c', function() alloc_log:check({a.di(dis.tes, 'tes')}) eq({test=10, tes=1.5}, dct2tbl(d)) eq(FAIL, check_emsg(function() return lib.tv_dict_add_float(d, 'testt', 3, 1.5) end, - 'E685: Internal error: hash_add()')) + 'E685: Internal error: hash_add(): duplicate key "tes"')) alloc_log:clear() lib.emsg_skip = lib.emsg_skip + 1 eq(FAIL, check_emsg(function() return lib.tv_dict_add_float(d, 'testt', 3, 1.5) end, @@ -2058,7 +2069,7 @@ describe('typval.c', function() }) eq({test=10, tes='TEST'}, dct2tbl(d)) eq(FAIL, check_emsg(function() return lib.tv_dict_add_str(d, 'testt', 3, 'TEST') end, - 'E685: Internal error: hash_add()')) + 'E685: Internal error: hash_add(): duplicate key "tes"')) alloc_log:clear() lib.emsg_skip = lib.emsg_skip + 1 eq(FAIL, check_emsg(function() return lib.tv_dict_add_str(d, 'testt', 3, 'TEST') end, @@ -2088,7 +2099,7 @@ describe('typval.c', function() }) eq({test=10, tes='TEST'}, dct2tbl(d)) eq(FAIL, check_emsg(function() return lib.tv_dict_add_allocated_str(d, 'testt', 3, s2) end, - 'E685: Internal error: hash_add()')) + 'E685: Internal error: hash_add(): duplicate key "tes"')) alloc_log:clear() lib.emsg_skip = lib.emsg_skip + 1 eq(FAIL, check_emsg(function() return lib.tv_dict_add_allocated_str(d, 'testt', 3, s3) end, @@ -2834,14 +2845,14 @@ describe('typval.c', function() alloc_log:clear() for _, v in ipairs({ {lib.VAR_NUMBER, nil}, - {lib.VAR_FLOAT, 'E806: using Float as a String'}, - {lib.VAR_PARTIAL, 'E729: using Funcref as a String'}, - {lib.VAR_FUNC, 'E729: using Funcref as a String'}, - {lib.VAR_LIST, 'E730: using List as a String'}, - {lib.VAR_DICT, 'E731: using Dictionary as a String'}, + {lib.VAR_FLOAT, nil}, + {lib.VAR_PARTIAL, 'E729: Using a Funcref as a String'}, + {lib.VAR_FUNC, 'E729: Using a Funcref as a String'}, + {lib.VAR_LIST, 'E730: Using a List as a String'}, + {lib.VAR_DICT, 'E731: Using a Dictionary as a String'}, {lib.VAR_BOOL, nil}, {lib.VAR_SPECIAL, nil}, - {lib.VAR_UNKNOWN, 'E908: using an invalid value as a String'}, + {lib.VAR_UNKNOWN, 'E908: Using an invalid value as a String'}, }) do local typ = v[1] local emsg = v[2] @@ -2981,179 +2992,118 @@ describe('typval.c', function() end end) end) + + local function test_string_fn(input, fn) + for _, v in ipairs(input) do + -- Using to_cstr in place of Neovim allocated string, cannot + -- tv_clear() that. + local tv = ffi.gc(typvalt(v[1], v[2]), nil) + alloc_log:check({}) + local emsg = v[3] + local ret = v[4] + eq(ret, check_emsg(function() + local res, buf = fn(tv) + if tv.v_type == lib.VAR_NUMBER or tv.v_type == lib.VAR_FLOAT + or tv.v_type == lib.VAR_SPECIAL or tv.v_type == lib.VAR_BOOL then + eq(buf, res) + else + neq(buf, res) + end + if res ~= nil then + return ffi.string(res) + else + return nil + end + end, emsg)) + if emsg then + alloc_log:clear() + elseif tv.v_type == lib.VAR_FLOAT then + alloc_log:check({a.freed(alloc_log.null), a.freed(alloc_log.null)}) + else + alloc_log:check({}) + end + end + end describe('string()', function() itp('works', function() local buf = lib.tv_get_string(lua2typvalt(int(1))) local buf_chk = lib.tv_get_string_chk(lua2typvalt(int(1))) neq(buf, buf_chk) - for _, v in ipairs({ + test_string_fn({ {lib.VAR_NUMBER, {v_number=42}, nil, '42'}, {lib.VAR_STRING, {v_string=to_cstr('100500')}, nil, '100500'}, - {lib.VAR_FLOAT, {v_float=42.53}, 'E806: using Float as a String', ''}, - {lib.VAR_PARTIAL, {v_partial=NULL}, 'E729: using Funcref as a String', ''}, - {lib.VAR_FUNC, {v_string=NULL}, 'E729: using Funcref as a String', ''}, - {lib.VAR_LIST, {v_list=NULL}, 'E730: using List as a String', ''}, - {lib.VAR_DICT, {v_dict=NULL}, 'E731: using Dictionary as a String', ''}, + {lib.VAR_FLOAT, {v_float=42.53}, nil, '42.53'}, + {lib.VAR_PARTIAL, {v_partial=NULL}, 'E729: Using a Funcref as a String', ''}, + {lib.VAR_FUNC, {v_string=NULL}, 'E729: Using a Funcref as a String', ''}, + {lib.VAR_LIST, {v_list=NULL}, 'E730: Using a List as a String', ''}, + {lib.VAR_DICT, {v_dict=NULL}, 'E731: Using a Dictionary as a String', ''}, {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, nil, 'v:null'}, {lib.VAR_BOOL, {v_bool=lib.kBoolVarTrue}, nil, 'v:true'}, {lib.VAR_BOOL, {v_bool=lib.kBoolVarFalse}, nil, 'v:false'}, - {lib.VAR_UNKNOWN, nil, 'E908: using an invalid value as a String', ''}, - }) do - -- Using to_cstr in place of Neovim allocated string, cannot - -- tv_clear() that. - local tv = ffi.gc(typvalt(v[1], v[2]), nil) - alloc_log:check({}) - local emsg = v[3] - local ret = v[4] - eq(ret, check_emsg(function() - local res = lib.tv_get_string(tv) - if tv.v_type == lib.VAR_NUMBER or tv.v_type == lib.VAR_SPECIAL - or tv.v_type == lib.VAR_BOOL then - eq(buf, res) - else - neq(buf, res) - end - if res ~= nil then - return ffi.string(res) - else - return nil - end - end, emsg)) - if emsg then - alloc_log:clear() - else - alloc_log:check({}) - end - end + {lib.VAR_UNKNOWN, nil, 'E908: Using an invalid value as a String', ''}, + }, function(tv) + return lib.tv_get_string(tv), buf + end) end) end) describe('string_chk()', function() itp('works', function() local buf = lib.tv_get_string_chk(lua2typvalt(int(1))) - for _, v in ipairs({ + test_string_fn({ {lib.VAR_NUMBER, {v_number=42}, nil, '42'}, {lib.VAR_STRING, {v_string=to_cstr('100500')}, nil, '100500'}, - {lib.VAR_FLOAT, {v_float=42.53}, 'E806: using Float as a String', nil}, - {lib.VAR_PARTIAL, {v_partial=NULL}, 'E729: using Funcref as a String', nil}, - {lib.VAR_FUNC, {v_string=NULL}, 'E729: using Funcref as a String', nil}, - {lib.VAR_LIST, {v_list=NULL}, 'E730: using List as a String', nil}, - {lib.VAR_DICT, {v_dict=NULL}, 'E731: using Dictionary as a String', nil}, + {lib.VAR_FLOAT, {v_float=42.53}, nil, '42.53'}, + {lib.VAR_PARTIAL, {v_partial=NULL}, 'E729: Using a Funcref as a String', nil}, + {lib.VAR_FUNC, {v_string=NULL}, 'E729: Using a Funcref as a String', nil}, + {lib.VAR_LIST, {v_list=NULL}, 'E730: Using a List as a String', nil}, + {lib.VAR_DICT, {v_dict=NULL}, 'E731: Using a Dictionary as a String', nil}, {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, nil, 'v:null'}, {lib.VAR_BOOL, {v_bool=lib.kBoolVarTrue}, nil, 'v:true'}, {lib.VAR_BOOL, {v_bool=lib.kBoolVarFalse}, nil, 'v:false'}, - {lib.VAR_UNKNOWN, nil, 'E908: using an invalid value as a String', nil}, - }) do - -- Using to_cstr, cannot free with tv_clear - local tv = ffi.gc(typvalt(v[1], v[2]), nil) - alloc_log:check({}) - local emsg = v[3] - local ret = v[4] - eq(ret, check_emsg(function() - local res = lib.tv_get_string_chk(tv) - if tv.v_type == lib.VAR_NUMBER or tv.v_type == lib.VAR_SPECIAL - or tv.v_type == lib.VAR_BOOL then - eq(buf, res) - else - neq(buf, res) - end - if res ~= nil then - return ffi.string(res) - else - return nil - end - end, emsg)) - if emsg then - alloc_log:clear() - else - alloc_log:check({}) - end - end + {lib.VAR_UNKNOWN, nil, 'E908: Using an invalid value as a String', nil}, + }, function(tv) + return lib.tv_get_string_chk(tv), buf + end) end) end) describe('string_buf()', function() itp('works', function() - for _, v in ipairs({ + test_string_fn({ {lib.VAR_NUMBER, {v_number=42}, nil, '42'}, {lib.VAR_STRING, {v_string=to_cstr('100500')}, nil, '100500'}, - {lib.VAR_FLOAT, {v_float=42.53}, 'E806: using Float as a String', ''}, - {lib.VAR_PARTIAL, {v_partial=NULL}, 'E729: using Funcref as a String', ''}, - {lib.VAR_FUNC, {v_string=NULL}, 'E729: using Funcref as a String', ''}, - {lib.VAR_LIST, {v_list=NULL}, 'E730: using List as a String', ''}, - {lib.VAR_DICT, {v_dict=NULL}, 'E731: using Dictionary as a String', ''}, + {lib.VAR_FLOAT, {v_float=42.53}, nil, '42.53'}, + {lib.VAR_PARTIAL, {v_partial=NULL}, 'E729: Using a Funcref as a String', ''}, + {lib.VAR_FUNC, {v_string=NULL}, 'E729: Using a Funcref as a String', ''}, + {lib.VAR_LIST, {v_list=NULL}, 'E730: Using a List as a String', ''}, + {lib.VAR_DICT, {v_dict=NULL}, 'E731: Using a Dictionary as a String', ''}, {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, nil, 'v:null'}, {lib.VAR_BOOL, {v_bool=lib.kBoolVarTrue}, nil, 'v:true'}, {lib.VAR_BOOL, {v_bool=lib.kBoolVarFalse}, nil, 'v:false'}, - {lib.VAR_UNKNOWN, nil, 'E908: using an invalid value as a String', ''}, - }) do - -- Using to_cstr, cannot free with tv_clear - local tv = ffi.gc(typvalt(v[1], v[2]), nil) - alloc_log:check({}) - local emsg = v[3] - local ret = v[4] - eq(ret, check_emsg(function() - local buf = ffi.new('char[?]', lib.NUMBUFLEN, {0}) - local res = lib.tv_get_string_buf(tv, buf) - if tv.v_type == lib.VAR_NUMBER or tv.v_type == lib.VAR_SPECIAL - or tv.v_type == lib.VAR_BOOL then - eq(buf, res) - else - neq(buf, res) - end - if res ~= nil then - return ffi.string(res) - else - return nil - end - end, emsg)) - if emsg then - alloc_log:clear() - else - alloc_log:check({}) - end - end + {lib.VAR_UNKNOWN, nil, 'E908: Using an invalid value as a String', ''}, + }, function(tv) + local buf = ffi.new('char[?]', lib.NUMBUFLEN, {0}) + return lib.tv_get_string_buf(tv, buf), buf + end) end) end) describe('string_buf_chk()', function() itp('works', function() - for _, v in ipairs({ + test_string_fn({ {lib.VAR_NUMBER, {v_number=42}, nil, '42'}, {lib.VAR_STRING, {v_string=to_cstr('100500')}, nil, '100500'}, - {lib.VAR_FLOAT, {v_float=42.53}, 'E806: using Float as a String', nil}, - {lib.VAR_PARTIAL, {v_partial=NULL}, 'E729: using Funcref as a String', nil}, - {lib.VAR_FUNC, {v_string=NULL}, 'E729: using Funcref as a String', nil}, - {lib.VAR_LIST, {v_list=NULL}, 'E730: using List as a String', nil}, - {lib.VAR_DICT, {v_dict=NULL}, 'E731: using Dictionary as a String', nil}, + {lib.VAR_FLOAT, {v_float=42.53}, nil, '42.53'}, + {lib.VAR_PARTIAL, {v_partial=NULL}, 'E729: Using a Funcref as a String', nil}, + {lib.VAR_FUNC, {v_string=NULL}, 'E729: Using a Funcref as a String', nil}, + {lib.VAR_LIST, {v_list=NULL}, 'E730: Using a List as a String', nil}, + {lib.VAR_DICT, {v_dict=NULL}, 'E731: Using a Dictionary as a String', nil}, {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, nil, 'v:null'}, {lib.VAR_BOOL, {v_bool=lib.kBoolVarTrue}, nil, 'v:true'}, {lib.VAR_BOOL, {v_bool=lib.kBoolVarFalse}, nil, 'v:false'}, - {lib.VAR_UNKNOWN, nil, 'E908: using an invalid value as a String', nil}, - }) do - -- Using to_cstr, cannot free with tv_clear - local tv = ffi.gc(typvalt(v[1], v[2]), nil) - alloc_log:check({}) - local emsg = v[3] - local ret = v[4] - eq(ret, check_emsg(function() - local buf = ffi.new('char[?]', lib.NUMBUFLEN, {0}) - local res = lib.tv_get_string_buf_chk(tv, buf) - if tv.v_type == lib.VAR_NUMBER or tv.v_type == lib.VAR_SPECIAL - or tv.v_type == lib.VAR_BOOL then - eq(buf, res) - else - neq(buf, res) - end - if res ~= nil then - return ffi.string(res) - else - return nil - end - end, emsg)) - if emsg then - alloc_log:clear() - else - alloc_log:check({}) - end - end + {lib.VAR_UNKNOWN, nil, 'E908: Using an invalid value as a String', nil}, + }, function(tv) + local buf = ffi.new('char[?]', lib.NUMBUFLEN, {0}) + return lib.tv_get_string_buf_chk(tv, buf), buf + end) end) end) end) diff --git a/test/unit/fixtures/multiqueue.c b/test/unit/fixtures/multiqueue.c index a8dca0a844..3a5ddab4b8 100644 --- a/test/unit/fixtures/multiqueue.c +++ b/test/unit/fixtures/multiqueue.c @@ -1,19 +1,16 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check -// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - #include <string.h> #include <stdlib.h> #include "nvim/event/multiqueue.h" #include "multiqueue.h" -void ut_multiqueue_put(MultiQueue *this, const char *str) +void ut_multiqueue_put(MultiQueue *self, const char *str) { - multiqueue_put(this, NULL, 1, str); + multiqueue_put(self, NULL, 1, str); } -const char *ut_multiqueue_get(MultiQueue *this) +const char *ut_multiqueue_get(MultiQueue *self) { - Event event = multiqueue_get(this); + Event event = multiqueue_get(self); return event.argv[0]; } diff --git a/test/unit/fixtures/rbuffer.c b/test/unit/fixtures/rbuffer.c index efa7ab1986..d587d6b054 100644 --- a/test/unit/fixtures/rbuffer.c +++ b/test/unit/fixtures/rbuffer.c @@ -1,6 +1,3 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check -// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - #include "nvim/rbuffer.h" #include "rbuffer.h" @@ -15,7 +12,7 @@ void ut_rbuffer_each_read_chunk(RBuffer *buf, each_ptr_cb cb) void ut_rbuffer_each_write_chunk(RBuffer *buf, each_ptr_cb cb) { - RBUFFER_UNTIL_FULL(buf, wptr, wcnt) { // -V1044 + RBUFFER_UNTIL_FULL(buf, wptr, wcnt) { cb(wptr, wcnt); rbuffer_produced(buf, wcnt); } diff --git a/test/unit/formatc.lua b/test/unit/formatc.lua index 2fd37c599a..c94f0d88f7 100644 --- a/test/unit/formatc.lua +++ b/test/unit/formatc.lua @@ -154,6 +154,8 @@ local C_keywords = set { -- luacheck: ignore -- -- The first one will have a lot of false positives (the line '{' for -- example), the second one is more unique. +--- @param string +--- @return string local function formatc(str) local toks = TokeniseC(str) local result = {} diff --git a/test/unit/helpers.lua b/test/unit/helpers.lua index 14d8eda357..8d581dac49 100644 --- a/test/unit/helpers.lua +++ b/test/unit/helpers.lua @@ -14,20 +14,15 @@ local map = global_helpers.tbl_map local eq = global_helpers.eq local trim = global_helpers.trim --- C constants. -local NULL = ffi.cast('void*', 0) - -local OK = 1 -local FAIL = 0 - -local cimport - -- add some standard header locations for _, p in ipairs(Paths.include_paths) do Preprocess.add_to_include_path(p) end -local child_pid = nil +local child_pid = nil --- @type integer +--- @generic F: function +--- @param func F +--- @return F local function only_separate(func) return function(...) if child_pid ~= 0 then @@ -36,9 +31,20 @@ local function only_separate(func) return func(...) end end -local child_calls_init = {} -local child_calls_mod = nil -local child_calls_mod_once = nil + +--- @class ChildCall +--- @field func function +--- @field args any[] + +--- @class ChildCallLog +--- @field func string +--- @field args any[] +--- @field ret any? + +local child_calls_init = {} --- @type ChildCall[] +local child_calls_mod = nil --- @type ChildCall[] +local child_calls_mod_once = nil --- @type ChildCall[]? + local function child_call(func, ret) return function(...) local child_calls = child_calls_mod or child_calls_init @@ -53,16 +59,16 @@ end -- Run some code at the start of the child process, before running the test -- itself. Is supposed to be run in `before_each`. +--- @param func function local function child_call_once(func, ...) if child_pid ~= 0 then - child_calls_mod_once[#child_calls_mod_once + 1] = { - func=func, args={...}} + child_calls_mod_once[#child_calls_mod_once + 1] = { func = func, args = {...} } else func(...) end end -local child_cleanups_mod_once = nil +local child_cleanups_mod_once = nil --- @type ChildCall[]? -- Run some code at the end of the child process, before exiting. Is supposed to -- be run in `before_each` because `after_each` is run after child has exited. @@ -75,7 +81,8 @@ local function child_cleanup_once(func, ...) end end -local libnvim = nil +-- Unittests are run from debug nvim binary in lua interpreter mode. +local libnvim = ffi.C local lib = setmetatable({}, { __index = only_separate(function(_, idx) @@ -87,13 +94,9 @@ local lib = setmetatable({}, { }) local init = only_separate(function() - -- load neovim shared library - libnvim = ffi.load(Paths.test_libnvim_path) for _, c in ipairs(child_calls_init) do c.func(unpack(c.args)) end - libnvim.time_init() - libnvim.fs_init() libnvim.event_init() libnvim.early_init(nil) if child_calls_mod then @@ -126,15 +129,19 @@ 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. +--- @param body string local function filter_complex_blocks(body) - local result = {} + local result = {} --- @type string[] for line in body:gmatch("[^\r\n]+") do if not (string.find(line, "(^)", 1, true) ~= nil or string.find(line, "_ISwupper", 1, true) or string.find(line, "_Float") + or string.find(line, "__s128") + or string.find(line, "__u128") or string.find(line, "msgpack_zone_push_finalizer") or string.find(line, "msgpack_unpacker_reserve_buffer") + or string.find(line, "value_init_") or string.find(line, "UUID_NULL") -- static const uuid_t UUID_NULL = {...} or string.find(line, "inline _Bool")) then result[#result + 1] = line @@ -149,19 +156,25 @@ local cdef = ffi.cdef local cimportstr -local previous_defines_init = '' -local preprocess_cache_init = {} +local previous_defines_init = [[ +typedef struct { char bytes[16]; } __attribute__((aligned(16))) __uint128_t; +typedef struct { char bytes[16]; } __attribute__((aligned(16))) __float128; +]] + +local preprocess_cache_init = {} --- @type table<string,string> local previous_defines_mod = '' -local preprocess_cache_mod = nil +local preprocess_cache_mod = nil --- @type table<string,string> local function is_child_cdefs() - return (os.getenv('NVIM_TEST_MAIN_CDEFS') ~= '1') + return os.getenv('NVIM_TEST_MAIN_CDEFS') ~= '1' end -- use this helper to import C files, you can pass multiple paths at once, -- this helper will return the C namespace of the nvim library. -cimport = function(...) - local previous_defines, preprocess_cache, cdefs +local function cimport(...) + local previous_defines --- @type string + local preprocess_cache --- @type table<string,string> + local cdefs if is_child_cdefs() and preprocess_cache_mod then preprocess_cache = preprocess_cache_mod previous_defines = previous_defines_mod @@ -177,7 +190,7 @@ cimport = function(...) path = './' .. path end if not preprocess_cache[path] then - local body + local body --- @type string body, previous_defines = Preprocess.preprocess(previous_defines, path) -- format it (so that the lines are "unique" statements), also filter out -- Objective-C blocks @@ -199,6 +212,7 @@ cimport = function(...) -- (they are needed in the right order with the struct definitions, -- otherwise luajit has wrong memory layouts for the sturcts) if line:match("#pragma%s+pack") then + --- @type string line = line .. " // " .. pragma_pack_id pragma_pack_id = pragma_pack_id + 1 end @@ -226,20 +240,21 @@ cimport = function(...) return lib end -local cimport_immediate = function(...) +local function cimport_immediate(...) local saved_pid = child_pid child_pid = 0 local err, emsg = pcall(cimport, ...) child_pid = saved_pid if not err then - emsg = tostring(emsg) - io.stderr:write(emsg .. '\n') + io.stderr:write(tostring(emsg) .. '\n') assert(false) else return lib end end +--- @param preprocess_cache table<string,string[]> +--- @param path string local function _cimportstr(preprocess_cache, path) if imported:contains(path) then return lib @@ -262,12 +277,14 @@ end local function alloc_log_new() local log = { - log={}, - lib=cimport('./src/nvim/memory.h'), - original_functions={}, + log={}, --- @type ChildCallLog[] + lib=cimport('./src/nvim/memory.h'), --- @type table<string,function> + original_functions={}, --- @type table<string,function> null={['\0:is_null']=true}, } + local allocator_functions = {'malloc', 'free', 'calloc', 'realloc'} + function log:save_original_functions() for _, funcname in ipairs(allocator_functions) do if not self.original_functions[funcname] then @@ -275,13 +292,16 @@ local function alloc_log_new() end end end + log.save_original_functions = child_call(log.save_original_functions) + function log:set_mocks() for _, k in ipairs(allocator_functions) do do local kk = k self.lib['mem_' .. k] = function(...) - local log_entry = {func=kk, args={...}} + --- @type ChildCallLog + local log_entry = { func = kk, args = {...} } self.log[#self.log + 1] = log_entry if kk == 'free' then self.original_functions[kk](...) @@ -302,17 +322,21 @@ local function alloc_log_new() end end end + log.set_mocks = child_call(log.set_mocks) + function log:clear() self.log = {} end + function log:check(exp) eq(exp, self.log) self:clear() end + function log:clear_tmp_allocs(clear_null_frees) - local toremove = {} - local allocs = {} + local toremove = {} --- @type integer[] + local allocs = {} --- @type table<string,integer> for i, v in ipairs(self.log) do if v.func == 'malloc' or v.func == 'calloc' then allocs[tostring(v.ret)] = i @@ -335,26 +359,20 @@ local function alloc_log_new() table.remove(self.log, toremove[i]) end end - function log:restore_original_functions() - -- Do nothing: set mocks live in a separate process - return - --[[ - [ for k, v in pairs(self.original_functions) do - [ self.lib['mem_' .. k] = v - [ end - ]] - end + function log:setup() log:save_original_functions() log:set_mocks() end + function log:before_each() - return end + function log:after_each() - log:restore_original_functions() end + log:setup() + return log end @@ -371,98 +389,109 @@ local function to_cstr(string) end cimport_immediate('./test/unit/fixtures/posix.h') -local sc = { - fork = function() - return tonumber(ffi.C.fork()) - end, - pipe = function() - local ret = ffi.new('int[2]', {-1, -1}) - ffi.errno(0) - local res = ffi.C.pipe(ret) - if (res ~= 0) then + +local sc = {} + +function sc.fork() + return tonumber(ffi.C.fork()) +end + +function sc.pipe() + local ret = ffi.new('int[2]', {-1, -1}) + ffi.errno(0) + local res = ffi.C.pipe(ret) + if (res ~= 0) then + local err = ffi.errno(0) + assert(res == 0, ("pipe() error: %u: %s"):format( + err, ffi.string(ffi.C.strerror(err)))) + end + assert(ret[0] ~= -1 and ret[1] ~= -1) + return ret[0], ret[1] +end + +--- @return string +function sc.read(rd, len) + local ret = ffi.new('char[?]', len, {0}) + local total_bytes_read = 0 + ffi.errno(0) + while total_bytes_read < len do + local bytes_read = tonumber(ffi.C.read( + rd, + ffi.cast('void*', ret + total_bytes_read), + len - total_bytes_read)) + if bytes_read == -1 then local err = ffi.errno(0) - assert(res == 0, ("pipe() error: %u: %s"):format( - err, ffi.string(ffi.C.strerror(err)))) - end - assert(ret[0] ~= -1 and ret[1] ~= -1) - return ret[0], ret[1] - end, - read = function(rd, len) - local ret = ffi.new('char[?]', len, {0}) - local total_bytes_read = 0 - ffi.errno(0) - while total_bytes_read < len do - local bytes_read = tonumber(ffi.C.read( - rd, - ffi.cast('void*', ret + total_bytes_read), - len - total_bytes_read)) - if bytes_read == -1 then - local err = ffi.errno(0) - if err ~= ffi.C.kPOSIXErrnoEINTR then - assert(false, ("read() error: %u: %s"):format( - err, ffi.string(ffi.C.strerror(err)))) - end - elseif bytes_read == 0 then - break - else - total_bytes_read = total_bytes_read + bytes_read + if err ~= ffi.C.kPOSIXErrnoEINTR then + assert(false, ("read() error: %u: %s"):format( + err, ffi.string(ffi.C.strerror(err)))) end + elseif bytes_read == 0 then + break + else + total_bytes_read = total_bytes_read + bytes_read end - return ffi.string(ret, total_bytes_read) - end, - write = function(wr, s) - local wbuf = to_cstr(s) - local total_bytes_written = 0 - ffi.errno(0) - while total_bytes_written < #s do - local bytes_written = tonumber(ffi.C.write( - wr, - ffi.cast('void*', wbuf + total_bytes_written), - #s - total_bytes_written)) - if bytes_written == -1 then - local err = ffi.errno(0) - if err ~= ffi.C.kPOSIXErrnoEINTR then - assert(false, ("write() error: %u: %s ('%s')"):format( - err, ffi.string(ffi.C.strerror(err)), s)) - end - elseif bytes_written == 0 then - break - else - total_bytes_written = total_bytes_written + bytes_written + end + return ffi.string(ret, total_bytes_read) +end + +function sc.write(wr, s) + local wbuf = to_cstr(s) + local total_bytes_written = 0 + ffi.errno(0) + while total_bytes_written < #s do + local bytes_written = tonumber(ffi.C.write( + wr, + ffi.cast('void*', wbuf + total_bytes_written), + #s - total_bytes_written)) + if bytes_written == -1 then + local err = ffi.errno(0) + if err ~= ffi.C.kPOSIXErrnoEINTR then + assert(false, ("write() error: %u: %s ('%s')"):format( + err, ffi.string(ffi.C.strerror(err)), s)) end + elseif bytes_written == 0 then + break + else + total_bytes_written = total_bytes_written + bytes_written end - return total_bytes_written - end, - close = ffi.C.close, - wait = function(pid) - ffi.errno(0) - local stat_loc = ffi.new('int[1]', {0}) - while true do - local r = ffi.C.waitpid(pid, stat_loc, ffi.C.kPOSIXWaitWUNTRACED) - if r == -1 then - local err = ffi.errno(0) - if err == ffi.C.kPOSIXErrnoECHILD then - break - elseif err ~= ffi.C.kPOSIXErrnoEINTR then - assert(false, ("waitpid() error: %u: %s"):format( - err, ffi.string(ffi.C.strerror(err)))) - end - else - assert(r == pid) + end + return total_bytes_written +end + +sc.close = ffi.C.close + +--- @param pid integer +--- @return integer +function sc.wait(pid) + ffi.errno(0) + local stat_loc = ffi.new('int[1]', {0}) + while true do + local r = ffi.C.waitpid(pid, stat_loc, ffi.C.kPOSIXWaitWUNTRACED) + if r == -1 then + local err = ffi.errno(0) + if err == ffi.C.kPOSIXErrnoECHILD then + break + elseif err ~= ffi.C.kPOSIXErrnoEINTR then + assert(false, ("waitpid() error: %u: %s"):format( + err, ffi.string(ffi.C.strerror(err)))) end + else + assert(r == pid) end - return stat_loc[0] - end, - exit = ffi.C._exit, -} + end + return stat_loc[0] +end +sc.exit = ffi.C._exit + +--- @param lst string[] +--- @return string local function format_list(lst) - local ret = '' + local ret = {} --- @type string[] for _, v in ipairs(lst) do - if ret ~= '' then ret = ret .. ', ' end - ret = ret .. assert:format({v, n=1})[1] + ret[#ret+1] = assert:format({v, n=1})[1] end - return ret + return table.concat(ret, ', ') end if os.getenv('NVIM_TEST_PRINT_SYSCALLS') == '1' then @@ -510,19 +539,26 @@ local tracehelp = dedent([[ ]]) local function child_sethook(wr) - local trace_level = os.getenv('NVIM_TEST_TRACE_LEVEL') - if not trace_level or trace_level == '' then - trace_level = 0 - else - trace_level = tonumber(trace_level) + local trace_level_str = os.getenv('NVIM_TEST_TRACE_LEVEL') + local trace_level = 0 + if trace_level_str and trace_level_str ~= '' then + --- @type number + trace_level = assert(tonumber(trace_level_str)) end + if trace_level <= 0 then return end + local trace_only_c = trace_level <= 1 + --- @type debuginfo?, string?, integer local prev_info, prev_reason, prev_lnum + + --- @param reason string + --- @param lnum integer + --- @param use_prev boolean local function hook(reason, lnum, use_prev) - local info = nil + local info = nil --- @type debuginfo? if use_prev then info = prev_info elseif reason ~= 'tail return' then -- tail return @@ -530,6 +566,7 @@ local function child_sethook(wr) end if trace_only_c and (not info or info.what ~= 'C') and not use_prev then + --- @cast info -nil if info.source:sub(-9) == '_spec.lua' then prev_info = info prev_reason = 'saved' @@ -570,12 +607,8 @@ local function child_sethook(wr) end -- assert(-1 <= lnum and lnum <= 99999) - local lnum_s - if lnum == -1 then - lnum_s = 'nknwn' - else - lnum_s = ('%u'):format(lnum) - end + local lnum_s = lnum == -1 and 'nknwn' or ('%u'):format(lnum) + --- @type string local msg = ( -- lua does not support %* '' .. msgchar @@ -597,6 +630,7 @@ end local trace_end_msg = ('E%s\n'):format((' '):rep(hook_msglen - 2)) +--- @type function local _debug_log local debug_log = only_separate(function(...) @@ -604,6 +638,7 @@ local debug_log = only_separate(function(...) end) local function itp_child(wr, func) + --- @param s string _debug_log = function(s) s = s:sub(1, hook_msglen - 2) sc.write(wr, '>' .. s .. (' '):rep(hook_msglen - 2 - #s) .. '\n') @@ -635,7 +670,7 @@ local function itp_child(wr, func) end local function check_child_err(rd) - local trace = {} + local trace = {} --- @type string[] local did_traceline = false local maxtrace = tonumber(os.getenv('NVIM_TEST_MAXTRACE')) or 1024 while true do @@ -665,11 +700,14 @@ local function check_child_err(rd) local len = tonumber(len_s) neq(0, len) if os.getenv('NVIM_TEST_TRACE_ON_ERROR') == '1' and #trace ~= 0 then + --- @type string err = '\nTest failed, trace:\n' .. tracehelp for _, traceline in ipairs(trace) do + --- @type string err = err .. traceline end end + --- @type string err = err .. sc.read(rd, len + 1) end local eres = sc.read(rd, 2) @@ -683,10 +721,12 @@ local function check_child_err(rd) end end if not did_traceline then + --- @type string err = err .. '\nNo end of trace occurred' end local cc_err, cc_emsg = pcall(check_cores, Paths.test_luajit_prg, true) if not cc_err then + --- @type string err = err .. '\ncheck_cores failed: ' .. cc_emsg end end @@ -811,6 +851,17 @@ local function ptr2key(ptr) return ffi.string(s) end +local function is_asan() + cimport('./src/nvim/version.h') + local status, res = pcall(function() return lib.version_cflags end) + if status then + return ffi.string(res):match('-fsanitize=[a-z,]*address') + else + return false + end +end + +--- @class test.unit.helpers.module local module = { cimport = cimport, cppimport = cppimport, @@ -819,9 +870,9 @@ local module = { lib = lib, cstr = cstr, to_cstr = to_cstr, - NULL = NULL, - OK = OK, - FAIL = FAIL, + NULL = ffi.cast('void*', 0), + OK = 1, + FAIL = 0, alloc_log_new = alloc_log_new, gen_itp = gen_itp, only_separate = only_separate, @@ -838,8 +889,11 @@ local module = { ptr2addr = ptr2addr, ptr2key = ptr2key, debug_log = debug_log, + is_asan = is_asan, } +--- @class test.unit.helpers: test.unit.helpers.module, test.helpers module = global_helpers.tbl_extend('error', module, global_helpers) + return function() return module end diff --git a/test/unit/keycodes_spec.lua b/test/unit/keycodes_spec.lua index 5bf27c9232..4da3d37aaa 100644 --- a/test/unit/keycodes_spec.lua +++ b/test/unit/keycodes_spec.lua @@ -5,7 +5,7 @@ local ffi = helpers.ffi local eq = helpers.eq local neq = helpers.neq -local keymap = helpers.cimport('./src/nvim/keycodes.h') +local keycodes = helpers.cimport('./src/nvim/keycodes.h') local NULL = helpers.NULL describe('keycodes.c', function() @@ -16,12 +16,12 @@ describe('keycodes.c', function() itp('no keycode', function() srcp[0] = 'abc' - eq(0, keymap.find_special_key(srcp, 3, modp, 0, NULL)) + eq(0, keycodes.find_special_key(srcp, 3, modp, 0, NULL)) end) itp('keycode with multiple modifiers', function() srcp[0] = '<C-M-S-A>' - neq(0, keymap.find_special_key(srcp, 9, modp, 0, NULL)) + neq(0, keycodes.find_special_key(srcp, 9, modp, 0, NULL)) neq(0, modp[0]) end) @@ -29,22 +29,22 @@ describe('keycodes.c', function() -- Compare other capitalizations to this. srcp[0] = '<C-A>' local all_caps_key = - keymap.find_special_key(srcp, 5, modp, 0, NULL) + keycodes.find_special_key(srcp, 5, modp, 0, NULL) local all_caps_mod = modp[0] srcp[0] = '<C-a>' eq(all_caps_key, - keymap.find_special_key(srcp, 5, modp, 0, NULL)) + keycodes.find_special_key(srcp, 5, modp, 0, NULL)) eq(all_caps_mod, modp[0]) srcp[0] = '<c-A>' eq(all_caps_key, - keymap.find_special_key(srcp, 5, modp, 0, NULL)) + keycodes.find_special_key(srcp, 5, modp, 0, NULL)) eq(all_caps_mod, modp[0]) srcp[0] = '<c-a>' eq(all_caps_key, - keymap.find_special_key(srcp, 5, modp, 0, NULL)) + keycodes.find_special_key(srcp, 5, modp, 0, NULL)) eq(all_caps_mod, modp[0]) end) @@ -52,20 +52,20 @@ describe('keycodes.c', function() -- Unescaped with in_string=false srcp[0] = '<C-">' eq(string.byte('"'), - keymap.find_special_key(srcp, 5, modp, 0, NULL)) + keycodes.find_special_key(srcp, 5, modp, 0, NULL)) -- Unescaped with in_string=true - eq(0, keymap.find_special_key(srcp, 5, modp, keymap.FSK_IN_STRING, NULL)) + eq(0, keycodes.find_special_key(srcp, 5, modp, keycodes.FSK_IN_STRING, NULL)) -- Escaped with in_string=false srcp[0] = '<C-\\">' -- Should fail because the key is invalid -- (more than 1 non-modifier character). - eq(0, keymap.find_special_key(srcp, 6, modp, 0, NULL)) + eq(0, keycodes.find_special_key(srcp, 6, modp, 0, NULL)) -- Escaped with in_string=true eq(string.byte('"'), - keymap.find_special_key(srcp, 6, modp, keymap.FSK_IN_STRING, NULL)) + keycodes.find_special_key(srcp, 6, modp, keycodes.FSK_IN_STRING, NULL)) end) end) diff --git a/test/unit/marktree_spec.lua b/test/unit/marktree_spec.lua index 3c96bc5f58..3f9dd4df12 100644 --- a/test/unit/marktree_spec.lua +++ b/test/unit/marktree_spec.lua @@ -87,13 +87,18 @@ local function dosplice(tree, shadow, start, old_extent, new_extent) shadowsplice(shadow, start, old_extent, new_extent) end +local ns = 10 local last_id = nil -local function put(tree, row, col, gravitate) +local function put(tree, row, col, gravitate, end_row, end_col, end_gravitate) last_id = last_id + 1 local my_id = last_id - lib.marktree_put_test(tree, my_id, row, col, gravitate); + end_row = end_row or -1 + end_col = end_col or -1 + end_gravitate = end_gravitate or false + + lib.marktree_put_test(tree, ns, my_id, row, col, gravitate, end_row, end_col, end_gravitate); return my_id end @@ -102,7 +107,7 @@ describe('marktree', function() last_id = 0 end) - itp('works', function() + itp('works', function() local tree = ffi.new("MarkTree[1]") -- zero initialized by luajit local shadow = {} local iter = ffi.new("MarkTreeIter[1]") @@ -129,7 +134,7 @@ describe('marktree', function() eq({}, id2pos) for i,ipos in pairs(shadow) do - local p = lib.marktree_lookup_ns(tree, -1, i, false, iter) + local p = lib.marktree_lookup_ns(tree, ns, i, false, iter) eq(ipos[1], p.pos.row) eq(ipos[2], p.pos.col) local k = lib.marktree_itr_current(iter) @@ -210,10 +215,330 @@ describe('marktree', function() lib.marktree_itr_get(tree, 10, 10, iter) lib.marktree_del_itr(tree, iter, false) - eq(11, iter[0].node.key[iter[0].i].pos.col) + eq(11, iter[0].x.key[iter[0].i].pos.col) lib.marktree_itr_get(tree, 11, 11, iter) lib.marktree_del_itr(tree, iter, false) - eq(12, iter[0].node.key[iter[0].i].pos.col) - end) + eq(12, iter[0].x.key[iter[0].i].pos.col) + end) + + itp("'intersect_mov' function works correctly", function() + local function mov(x, y, w) + local xa = ffi.new("uint64_t[?]", #x) + for i, xi in ipairs(x) do xa[i-1] = xi end + local ya = ffi.new("uint64_t[?]", #y) + for i, yi in ipairs(y) do ya[i-1] = yi end + local wa = ffi.new("uint64_t[?]", #w) + for i, wi in ipairs(w) do wa[i-1] = wi end + + local dummy_size = #x + #y + #w + local wouta = ffi.new("uint64_t[?]", dummy_size) + local douta = ffi.new("uint64_t[?]", dummy_size) + local wsize = ffi.new("size_t[1]") + wsize[0] = dummy_size + local dsize = ffi.new("size_t[1]") + dsize[0] = dummy_size + + local status = lib.intersect_mov_test(xa, #x, ya, #y, wa, #w, wouta, wsize, douta, dsize) + if status == 0 then error'wowza' end + + local wout, dout = {}, {} + for i = 0,tonumber(wsize[0])-1 do table.insert(wout, tonumber(wouta[i])) end + for i = 0,tonumber(dsize[0])-1 do table.insert(dout, tonumber(douta[i])) end + return {wout, dout} + end + + eq({{}, {}}, mov({}, {2, 3}, {2, 3})) + eq({{2, 3}, {}}, mov({}, {}, {2, 3})) + eq({{2, 3}, {}}, mov({2, 3}, {}, {})) + eq({{}, {2,3}}, mov({}, {2,3}, {})) + + eq({{1, 5}, {}}, mov({1,2,5}, {2, 3}, {3})) + eq({{1, 2}, {}}, mov({1,2,5}, {5, 10}, {10})) + eq({{1, 2}, {5}}, mov({1,2}, {5, 10}, {10})) + eq({{1,3,5,7,9}, {2,4,6,8,10}}, mov({1,3,5,7,9}, {2,4,6,8,10}, {})) + eq({{1,3,5,7,9}, {2,6,10}}, mov({1,3,5,7,9}, {2,4,6,8,10}, {4, 8})) + eq({{1,4,7}, {2,5,8}}, mov({1,3,4,6,7,9}, {2,3,5,6,8,9}, {})) + eq({{1,4,7}, {}}, mov({1,3,4,6,7,9}, {2,3,5,6,8,9}, {2,5,8})) + eq({{0,1,4,7,10}, {}}, mov({1,3,4,6,7,9}, {2,3,5,6,8,9}, {0,2,5,8,10})) + end) + + + local function check_intersections(tree) + lib.marktree_check(tree) + -- to debug stuff disable this branch + if true == true then + ok(lib.marktree_check_intersections(tree)) + return + end + + local str1 = lib.mt_inspect(tree, true, true) + local dot1 = ffi.string(str1.data, str1.size) + + local val = lib.marktree_check_intersections(tree) + if not val then + local str2 = lib.mt_inspect(tree, true, true) + local dot2 = ffi.string(str2.data, str2.size) + print("actual:\n\n".."Xafile.dot".."\n\nexpected:\n\n".."Xefile.dot".."\n") + print("nivå", tree[0].root.level); + io.stdout:flush() + local afil = io.open("Xafile.dot", "wb") + afil:write(dot1) + afil:close() + local efil = io.open("Xefile.dot", "wb") + efil:write(dot2) + efil:close() + ok(false) + else + ffi.C.xfree(str1.data) + end + end + + itp('works with intersections', function() + local tree = ffi.new("MarkTree[1]") -- zero initialized by luajit + + local ids = {} + + for i = 1,80 do + table.insert(ids, put(tree, 1, i, false, 2, 100-i, false)) + check_intersections(tree) + end + for i = 1,80 do + lib.marktree_del_pair_test(tree, ns, ids[i]) + check_intersections(tree) + end + ids = {} + + for i = 1,80 do + table.insert(ids, put(tree, 1, i, false, 2, 100-i, false)) + check_intersections(tree) + end + + for i = 1,10 do + for j = 1,8 do + local ival = (j-1)*10+i + lib.marktree_del_pair_test(tree, ns, ids[ival]) + check_intersections(tree) + end + end + end) + + itp('works with intersections with a big tree', function() + local tree = ffi.new("MarkTree[1]") -- zero initialized by luajit + + local ids = {} + + for i = 1,1000 do + table.insert(ids, put(tree, 1, i, false, 2, 1000-i, false)) + if i % 10 == 1 then + check_intersections(tree) + end + end + + check_intersections(tree) + eq(2000, tree[0].n_keys) + ok(tree[0].root.level >= 2) + + local iter = ffi.new("MarkTreeIter[1]") + + local k = 0 + for i = 1,20 do + for j = 1,50 do + k = k + 1 + local ival = (j-1)*20+i + if false == true then -- if there actually is a failure, this branch will fail out at the actual spot of the error + lib.marktree_lookup_ns(tree, ns, ids[ival], false, iter) + lib.marktree_del_itr(tree, iter, false) + check_intersections(tree) + + lib.marktree_lookup_ns(tree, ns, ids[ival], true, iter) + lib.marktree_del_itr(tree, iter, false) + check_intersections(tree) + else + lib.marktree_del_pair_test(tree, ns, ids[ival]) + if k % 5 == 1 then + check_intersections(tree) + end + end + end + end + + eq(0, tree[0].n_keys) + end) + + itp('works with intersections and marktree_splice', function() + local tree = ffi.new("MarkTree[1]") -- zero initialized by luajit + + for i = 1,1000 do + put(tree, 1, i, false, 2, 1000-i, false) + if i % 10 == 1 then + check_intersections(tree) + end + end + + check_intersections(tree) + eq(2000, tree[0].n_keys) + ok(tree[0].root.level >= 2) + + for _ = 1,10 do + lib.marktree_splice(tree, 0, 0, 0, 100, 0, 0) + check_intersections(tree) + end + end) + + itp('marktree_move should preserve key order', function() + local tree = ffi.new("MarkTree[1]") -- zero initialized by luajit + local iter = ffi.new("MarkTreeIter[1]") + local ids = {} + + -- new index and old index look the same, but still have to move because + -- pos will get updated + table.insert(ids, put(tree, 1, 1, false, 1, 3, false)) + table.insert(ids, put(tree, 1, 3, false, 1, 3, false)) + table.insert(ids, put(tree, 1, 3, false, 1, 3, false)) + table.insert(ids, put(tree, 1, 3, false, 1, 3, false)) + + lib.marktree_lookup_ns(tree, ns, ids[3], false, iter) + lib.marktree_move(tree, iter, 1, 2) + + check_intersections(tree) + end) + + itp('works with intersections and marktree_move', function() + local tree = ffi.new("MarkTree[1]") -- zero initialized by luajit + + local ids = {} + + for i = 1,1000 do + table.insert(ids, put(tree, 1, i, false, 2, 1000-i, false)) + if i % 10 == 1 then + check_intersections(tree) + end + end + + local iter = ffi.new("MarkTreeIter[1]") + for i = 1,1000 do + local which = i%2 + lib.marktree_lookup_ns(tree, ns, ids[i], which, iter) + lib.marktree_move(tree, iter, 1+which, 500+i) + if i % 10 == 1 then + check_intersections(tree) + end + end + + end) + + itp('works with intersections with a even bigger tree', function() + local tree = ffi.new("MarkTree[1]") -- zero initialized by luajit + + local ids = {} + + -- too much overhead on ASAN + local size_factor = helpers.is_asan() and 3 or 10 + + local at_row = {} + for i = 1, 10 do + at_row[i] = {} + end + + local size = 1000*size_factor + local k = 1 + while k <= size do + for row1 = 1,9 do + for row2 = row1,10 do -- note row2 can be == row1, leads to empty ranges being tested when k > size/2 + if k > size then + break + end + local id = put(tree, row1, k, false, row2, size-k, false) + table.insert(ids, id) + for i = row1+1, row2 do + table.insert(at_row[i], id) + end + --if tree[0].root.level == 4 then error("kk"..k) end + if k % 100*size_factor == 1 or (k < 2000 and k%100 == 1) then + check_intersections(tree) + end + k = k + 1 + end + end + end + + eq(2*size, tree[0].n_keys) + ok(tree[0].root.level >= 3) + check_intersections(tree) + + local iter = ffi.new("MarkTreeIter[1]") + local pair = ffi.new("MTPair[1]") + for i = 1,10 do + -- use array as set and not {[id]=true} map, to detect duplicates + local set = {} + eq(true, ffi.C.marktree_itr_get_overlap(tree, i, 0, iter)) + while ffi.C.marktree_itr_step_overlap(tree, iter, pair) do + local id = tonumber(pair[0].start.id) + table.insert(set, id) + end + table.sort(set) + eq(at_row[i], set) + end + + k = 0 + for i = 1,100 do + for j = 1,(10*size_factor) do + k = k + 1 + local ival = (j-1)*100+i + lib.marktree_del_pair_test(tree, ns, ids[ival]) + -- just a few stickprov, if there is trouble we need to check + -- everyone using the code in the "big tree" case above + if k % 100*size_factor == 0 or (k > 3000 and k % 200 == 0) then + check_intersections(tree) + end + end + end + + eq(0, tree[0].n_keys) + end) + + itp('works with intersections with a even bigger tree and splice', function() + local tree = ffi.new("MarkTree[1]") -- zero initialized by luajit + + -- too much overhead on ASAN + local size_factor = helpers.is_asan() and 3 or 10 + + local at_row = {} + for i = 1, 10 do + at_row[i] = {} + end + + local size = 1000*size_factor + local k = 1 + while k <= size do + for row1 = 1,9 do + for row2 = row1,10 do -- note row2 can be == row1, leads to empty ranges being tested when k > size/2 + if k > size then + break + end + local id = put(tree, row1, k, false, row2, size-k, false) + for i = row1+1, row2 do + table.insert(at_row[i], id) + end + --if tree[0].root.level == 4 then error("kk"..k) end + if k % 100*size_factor == 1 or (k < 2000 and k%100 == 1) then + check_intersections(tree) + end + k = k + 1 + end + end + end + + eq(2*size, tree[0].n_keys) + ok(tree[0].root.level >= 3) + check_intersections(tree) + + for _ = 1,10 do + for j = 3, 8 do + lib.marktree_splice(tree, j, 0, 0, 200, 0, 0) + check_intersections(tree) + end + end + end) end) diff --git a/test/unit/mbyte_spec.lua b/test/unit/mbyte_spec.lua index fdb1bceab0..cd94624570 100644 --- a/test/unit/mbyte_spec.lua +++ b/test/unit/mbyte_spec.lua @@ -4,17 +4,9 @@ local itp = helpers.gen_itp(it) local ffi = helpers.ffi local eq = helpers.eq -local mbyte = helpers.cimport("./src/nvim/mbyte.h") -local charset = helpers.cimport('./src/nvim/charset.h') +local lib = helpers.cimport('./src/nvim/mbyte.h', './src/nvim/charset.h', './src/nvim/grid.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 = {} @@ -30,14 +22,14 @@ describe('mbyte', function() itp('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}))) + eq(c, lib.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}))) + --eq(0x00c0, lib.utf_ptr2char(to_string({0xc0, 0x80}))) + --eq(0x00c1, lib.utf_ptr2char(to_string({0xc1, 0xbf}))) -- -- Sequences with more than four bytes end) @@ -47,240 +39,133 @@ describe('mbyte', function() local char_p = ffi.typeof('char[?]') for c = n * 0x1000, n * 0x1000 + 0xFFF do local p = char_p(4, 0) - mbyte.utf_char2bytes(c, p) - eq(c, mbyte.utf_ptr2char(p)) - eq(charset.vim_iswordc(c), charset.vim_iswordp(p)) + lib.utf_char2bytes(c, p) + eq(c, lib.utf_ptr2char(p)) + eq(lib.vim_iswordc(c), lib.vim_iswordp(p)) end end) end - describe('utfc_ptr2char_len', function() + describe('utfc_ptr2schar_len', function() + local function test_seq(seq) + local firstc = ffi.new("int[1]") + local buf = ffi.new("char[32]") + lib.schar_get(buf, lib.utfc_ptr2schar_len(to_string(seq), #seq, firstc)) + return {ffi.string(buf), firstc[0]} + end + + local function byte(val) + return {string.char(val), val} + end itp('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]) + eq({'', 0}, test_seq{0}) + for c = 1, 127 do + eq(byte(c), test_seq{c}) + end + for c = 128, 255 do + eq({'', c}, test_seq{c}) end end) itp('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]) + eq(byte(0x7f), test_seq{0x7f, 0x7f}) -- No combining characters - pcc = to_intp() - eq(0x007f, mbyte.utfc_ptr2char_len(to_string({0x7f, 0x80}), pcc, 2)) - eq(0, pcc[0]) + eq(byte(0x7f), test_seq{0x7f, 0x80}) -- No UTF-8 sequence - pcc = to_intp() - eq(0x00c2, mbyte.utfc_ptr2char_len(to_string({0xc2, 0x7f}), pcc, 2)) - eq(0, pcc[0]) + eq({'', 0xc2}, test_seq{0xc2, 0x7f}) -- One UTF-8 character - pcc = to_intp() - eq(0x0080, mbyte.utfc_ptr2char_len(to_string({0xc2, 0x80}), pcc, 2)) - eq(0, pcc[0]) + eq({'\xc2\x80', 0x80}, test_seq{0xc2, 0x80}) -- No UTF-8 sequence - pcc = to_intp() - eq(0x00c2, mbyte.utfc_ptr2char_len(to_string({0xc2, 0xc0}), pcc, 2)) - eq(0, pcc[0]) + eq({'', 0xc2}, test_seq{0xc2, 0xc0}) end) itp('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]) + eq(byte(0x7f), test_seq{0x7f, 0x80, 0x80}) -- No combining character - pcc = to_intp() - eq(0x007f, mbyte.utfc_ptr2char_len(to_string({0x7f, 0xc2, 0x80}), pcc, 3)) - eq(0, pcc[0]) + eq(byte(0x7f), test_seq{0x7f, 0xc2, 0x80}) -- 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]) + eq({"\x7f\xcc\x80", 0x7f}, test_seq{0x7f, 0xcc, 0x80}) -- No UTF-8 sequence - pcc = to_intp() - eq(0x00c2, mbyte.utfc_ptr2char_len(to_string({0xc2, 0x7f, 0xcc}), pcc, 3)) - eq(0, pcc[0]) + eq({'', 0xc2}, test_seq{0xc2, 0x7f, 0xcc}) -- Incomplete combining character - pcc = to_intp() - eq(0x0080, mbyte.utfc_ptr2char_len(to_string({0xc2, 0x80, 0xcc}), pcc, 3)) - eq(0, pcc[0]) + eq({"\xc2\x80", 0x80}, test_seq{0xc2, 0x80, 0xcc}) - -- One UTF-8 character - pcc = to_intp() - eq(0x20d0, mbyte.utfc_ptr2char_len(to_string({0xe2, 0x83, 0x90}), pcc, 3)) - eq(0, pcc[0]) + -- One UTF-8 character (composing only) + eq({" \xe2\x83\x90", 0x20d0}, test_seq{0xe2, 0x83, 0x90}) end) itp('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]) + eq(byte(0x7f), test_seq{0x7f, 0x7f, 0xcc, 0x80}) -- 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]) + eq(byte(0x7f), test_seq{0x7f, 0xc2, 0xcc, 0x80}) -- 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]) + eq({"\x7f\xcc\x80", 0x7f}, test_seq{0x7f, 0xcc, 0x80, 0xcc}) -- 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]) + eq({'', 0xc2}, test_seq{0xc2, 0x7f, 0xcc, 0x80}) -- 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]) + eq({"\xc2\x80", 0x80}, test_seq{0xc2, 0x80, 0xcc, 0xcc}) -- 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]) + eq({"\xc2\x80\xcc\x81", 0x80}, test_seq{0xc2, 0x80, 0xcc, 0x81}) -- 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]) + eq({"\xf4\x80\x80\x80", 0x100000}, test_seq{0xf4, 0x80, 0x80, 0x80}) end) itp('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]) + eq(byte(0x7f), test_seq{0x7f, 0x7f, 0xcc, 0x80, 0x80}) -- 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]) + eq(byte(0x7f), test_seq{0x7f, 0xc2, 0xcc, 0x80, 0x80}) -- 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]) + eq({"\x7f\xcc\x80", 0x7f}, test_seq{0x7f, 0xcc, 0x80, 0xcc, 0x00}) -- 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]) + eq({"\x7f\xcc\x80\xcc\x81", 0x7f}, test_seq{0x7f, 0xcc, 0x80, 0xcc, 0x81}) -- 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]) + eq({"\x7f\xcc\x80\xcc\x81\xcc\x82", 0x7f}, test_seq{0x7f, 0xcc, 0x80, 0xcc, 0x81, 0xcc, 0x82}) -- 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]) + eq({"\x7f\xcc\x80\xcc\x81\xcc\x82\xcc\x83", 0x7f}, test_seq{0x7f, 0xcc, 0x80, 0xcc, 0x81, 0xcc, 0x82, 0xcc, 0x83}) -- 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]) + eq({"\x7f\xcc\x80\xcc\x81\xcc\x82\xcc\x83\xcc\x84", 0x7f}, test_seq{0x7f, 0xcc, 0x80, 0xcc, 0x81, 0xcc, 0x82, 0xcc, 0x83, 0xcc, 0x84}) + -- Combining characters U+0300, U+0301, U+0302, U+0303, U+0304, U+0305 + eq({"\x7f\xcc\x80\xcc\x81\xcc\x82\xcc\x83\xcc\x84\xcc\x85", 0x7f}, test_seq{0x7f, 0xcc, 0x80, 0xcc, 0x81, 0xcc, 0x82, 0xcc, 0x83, 0xcc, 0x84, 0xcc, 0x85}) - -- 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]) + -- Combining characters U+0300, U+0301, U+0302, U+0303, U+0304, U+0305, U+0306 + eq({"\x7f\xcc\x80\xcc\x81\xcc\x82\xcc\x83\xcc\x84\xcc\x85\xcc\x86", 0x7f}, test_seq{0x7f, 0xcc, 0x80, 0xcc, 0x81, 0xcc, 0x82, 0xcc, 0x83, 0xcc, 0x84, 0xcc, 0x85, 0xcc, 0x86}) + -- Only three following combining characters U+0300, U+0301, U+0302 + eq({"\x7f\xcc\x80\xcc\x81\xcc\x82", 0x7f}, test_seq{0x7f, 0xcc, 0x80, 0xcc, 0x81, 0xcc, 0x82, 0xc2, 0x80, 0xcc, 0x84, 0xcc, 0x85}) -- 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]) + eq({'', 0xc2}, test_seq{0xc2, 0x7f, 0xcc, 0x80, 0x80}) -- 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]) + eq({"\xc2\x80", 0x80}, test_seq{0xc2, 0x80, 0xcc, 0xcc, 0x80}) -- 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]) + eq({"\xc2\x80\xcc\x81", 0x80}, test_seq{0xc2, 0x80, 0xcc, 0x81, 0x7f}) -- 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]) + eq({"\xc2\x80\xcc\x81", 0x80}, test_seq{0xc2, 0x80, 0xcc, 0x81, 0xcc}) -- 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]) + eq({"\xf4\x80\x80\x80", 0x100000}, test_seq{0xf4, 0x80, 0x80, 0x80, 0x7f}) -- 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]) + eq({"\xf4\x80\x80\x80", 0x100000}, test_seq{0xf4, 0x80, 0x80, 0x80, 0x80}) -- 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]) + eq({"\xf4\x80\x80\x80", 0x100000}, test_seq{0xf4, 0x80, 0x80, 0x80, 0xcc}) -- 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]) + eq({"\xf4\x80\x80\x80\xe1\xaa\xb0\xcc\x81", 0x100000}, test_seq{0xf4, 0x80, 0x80, 0x80, 0xe1, 0xaa, 0xb0, 0xcc, 0x81}) end) end) diff --git a/test/unit/message_spec.lua b/test/unit/message_spec.lua index 549eff6e03..0d5268d199 100644 --- a/test/unit/message_spec.lua +++ b/test/unit/message_spec.lua @@ -12,7 +12,7 @@ describe('trunc_string', function() local buflen = 40 local function test_inplace(s, expected, room) room = room and room or 20 - local buf = cimp.xmalloc(ffi.sizeof('char_u') * buflen) + local buf = cimp.xmalloc(ffi.sizeof('char') * buflen) ffi.C.strcpy(buf, s) cimp.trunc_string(buf, buf, room, buflen) eq(expected, ffi.string(buf)) @@ -21,7 +21,7 @@ describe('trunc_string', function() local function test_copy(s, expected, room) room = room and room or 20 - local buf = cimp.xmalloc(ffi.sizeof('char_u') * buflen) + local buf = cimp.xmalloc(ffi.sizeof('char') * buflen) local str = cimp.xstrdup(to_cstr(s)) cimp.trunc_string(str, buf, room, buflen) eq(expected, ffi.string(buf)) diff --git a/test/unit/msgpack_spec.lua b/test/unit/msgpack_spec.lua new file mode 100644 index 0000000000..c573714714 --- /dev/null +++ b/test/unit/msgpack_spec.lua @@ -0,0 +1,75 @@ +local helpers = require('test.unit.helpers')(after_each) +local cimport = helpers.cimport +local itp = helpers.gen_itp(it) +local lib = cimport('./src/nvim/msgpack_rpc/unpacker.h', './src/nvim/memory.h') +local ffi = helpers.ffi +local eq = helpers.eq +local to_cstr = helpers.to_cstr + +--- @class Unpacker +--- @field read_ptr ffi.cdata* +--- @field read_size number + +--- @alias Unpacker* table<number, Unpacker> +--- @return Unpacker* unpacker `unpacker[0]` to dereference +local function make_unpacker() + return ffi.gc(ffi.cast('Unpacker*', lib.xcalloc(1, ffi.sizeof('Unpacker'))), function(unpacker) + lib.unpacker_teardown(unpacker, nil, nil) + lib.xfree(unpacker) + end) +end + +--- @param unpacker Unpacker* +--- @param data string +--- @param size number? *default: data:len()* +local function unpacker_goto(unpacker, data, size) + unpacker[0].read_ptr = to_cstr(data) + unpacker[0].read_size = size or data:len() +end + +--- @param unpacker Unpacker* +--- @return boolean +local function unpacker_advance(unpacker) + return lib.unpacker_advance(unpacker) +end + +describe('msgpack', function() + describe('unpacker', function() + itp('does not crash when paused between `cells` and `wrap` params of `grid_line` #25184', function() + -- [kMessageTypeNotification, "redraw", [ + -- ["grid_line", + -- [2, 0, 0, [[" " , 0, 77]], false] + -- ] + -- ]] + local payload = + '\x93\x02\xa6\x72\x65\x64\x72\x61\x77\x91\x92\xa9\x67\x72\x69\x64\x5f\x6c\x69\x6e\x65\x95\x02\x00\x00\x91\x93\xa1\x20\x00\x4d\xc2' + + local unpacker = make_unpacker() + lib.unpacker_init(unpacker) + + unpacker_goto(unpacker, payload, payload:len() - 1) + local finished = unpacker_advance(unpacker) + eq(finished, false) + + unpacker[0].read_size = unpacker[0].read_size + 1 + finished = unpacker_advance(unpacker) + eq(finished, true) + end) + + itp('does not crash when parsing grid_line event with 0 `cells` #25184', function() + local unpacker = make_unpacker() + lib.unpacker_init(unpacker) + + unpacker_goto(unpacker, + -- [kMessageTypeNotification, "redraw", [ + -- ["grid_line", + -- [2, 0, 0, [], false] + -- ] + -- ]] + '\x93\x02\xa6\x72\x65\x64\x72\x61\x77\x91\x92\xa9\x67\x72\x69\x64\x5f\x6c\x69\x6e\x65\x95\x02\x00\x00\x90\xc2' + ) + local finished = unpacker_advance(unpacker) + eq(finished, true) + end) + end) +end) diff --git a/test/unit/optionstr_spec.lua b/test/unit/optionstr_spec.lua index f8122f4fe0..2e7198a63a 100644 --- a/test/unit/optionstr_spec.lua +++ b/test/unit/optionstr_spec.lua @@ -4,10 +4,10 @@ local itp = helpers.gen_itp(it) local to_cstr = helpers.to_cstr local eq = helpers.eq -local option = helpers.cimport("./src/nvim/optionstr.h") +local optionstr = helpers.cimport("./src/nvim/optionstr.h") local check_ff_value = function(ff) - return option.check_ff_value(to_cstr(ff)) + return optionstr.check_ff_value(to_cstr(ff)) end describe('check_ff_value', function() diff --git a/test/unit/os/env_spec.lua b/test/unit/os/env_spec.lua index 71177f4c65..24b92edee5 100644 --- a/test/unit/os/env_spec.lua +++ b/test/unit/os/env_spec.lua @@ -10,8 +10,6 @@ local to_cstr = helpers.to_cstr local NULL = helpers.NULL local OK = 0 -require('lfs') - local cimp = cimport('./src/nvim/os/os.h') describe('env.c', function() diff --git a/test/unit/os/fileio_spec.lua b/test/unit/os/fileio_spec.lua index 4d58a8934e..fd30ca70da 100644 --- a/test/unit/os/fileio_spec.lua +++ b/test/unit/os/fileio_spec.lua @@ -1,4 +1,4 @@ -local lfs = require('lfs') +local luv = require('luv') local helpers = require('test.unit.helpers')(after_each) local itp = helpers.gen_itp(it) @@ -7,6 +7,7 @@ local eq = helpers.eq local ffi = helpers.ffi local cimport = helpers.cimport local cppimport = helpers.cppimport +local mkdir = helpers.mkdir local m = cimport('./src/nvim/os/os.h', './src/nvim/os/fileio.h') cppimport('fcntl.h') @@ -25,7 +26,7 @@ local linkb = dir .. '/broken.lnk' local filec = dir .. '/created-file.dat' before_each(function() - lfs.mkdir(dir); + mkdir(dir); local f1 = io.open(file1, 'w') f1:write(fcontents) @@ -35,8 +36,8 @@ before_each(function() f2:write(fcontents) f2:close() - lfs.link('file1.dat', linkf, true) - lfs.link('broken.dat', linkb, true) + luv.fs_symlink('file1.dat', linkf) + luv.fs_symlink('broken.dat', linkb) end) after_each(function() @@ -45,7 +46,7 @@ after_each(function() os.remove(linkf) os.remove(linkb) os.remove(filec) - lfs.rmdir(dir) + luv.fs_rmdir(dir) end) local function file_open(fname, flags, mode) @@ -119,13 +120,13 @@ describe('file_open_fd', function() eq(0, m.file_close(fp, false)) end) itp('can use file descriptor returned by os_open for writing', function() - eq(nil, lfs.attributes(filec)) + eq(nil, luv.fs_stat(filec)) local fd = m.os_open(filec, m.kO_WRONLY + m.kO_CREAT, 384) local err, fp = file_open_fd(fd, m.kFileWriteOnly) eq(0, err) eq(4, file_write(fp, 'test')) eq(0, m.file_close(fp, false)) - eq(4, lfs.attributes(filec).size) + eq(4, luv.fs_stat(filec).size) eq('test', io.open(filec):read('*a')) end) end) @@ -139,13 +140,13 @@ describe('file_open_fd_new', function() eq(0, m.file_free(fp, false)) end) itp('can use file descriptor returned by os_open for writing', function() - eq(nil, lfs.attributes(filec)) + eq(nil, luv.fs_stat(filec)) local fd = m.os_open(filec, m.kO_WRONLY + m.kO_CREAT, 384) local err, fp = file_open_fd_new(fd, m.kFileWriteOnly) eq(0, err) eq(4, file_write(fp, 'test')) eq(0, m.file_free(fp, false)) - eq(4, lfs.attributes(filec).size) + eq(4, luv.fs_stat(filec).size) eq('test', io.open(filec):read('*a')) end) end) @@ -154,32 +155,32 @@ describe('file_open', function() itp('can create a rwx------ file with kFileCreate', function() local err, fp = file_open(filec, m.kFileCreate, 448) eq(0, err) - local attrs = lfs.attributes(filec) - eq('rwx------', attrs.permissions) + local attrs = luv.fs_stat(filec) + eq(33216, attrs.mode) eq(0, m.file_close(fp, false)) end) itp('can create a rw------- file with kFileCreate', function() local err, fp = file_open(filec, m.kFileCreate, 384) eq(0, err) - local attrs = lfs.attributes(filec) - eq('rw-------', attrs.permissions) + local attrs = luv.fs_stat(filec) + eq(33152, attrs.mode) eq(0, m.file_close(fp, false)) end) itp('can create a rwx------ file with kFileCreateOnly', function() local err, fp = file_open(filec, m.kFileCreateOnly, 448) eq(0, err) - local attrs = lfs.attributes(filec) - eq('rwx------', attrs.permissions) + local attrs = luv.fs_stat(filec) + eq(33216, attrs.mode) eq(0, m.file_close(fp, false)) end) itp('can create a rw------- file with kFileCreateOnly', function() local err, fp = file_open(filec, m.kFileCreateOnly, 384) eq(0, err) - local attrs = lfs.attributes(filec) - eq('rw-------', attrs.permissions) + local attrs = luv.fs_stat(filec) + eq(33152, attrs.mode) eq(0, m.file_close(fp, false)) end) @@ -228,7 +229,7 @@ describe('file_open', function() eq(0, err) eq(true, fp.wr) eq(0, m.file_close(fp, false)) - local attrs = lfs.attributes(file1) + local attrs = luv.fs_stat(file1) eq(0, attrs.size) end) @@ -237,14 +238,14 @@ describe('file_open', function() eq(0, err) eq(true, fp.wr) eq(0, m.file_close(fp, false)) - local attrs = lfs.attributes(file1) + local attrs = luv.fs_stat(file1) eq(4096, attrs.size) end) itp('fails to create a file with just kFileWriteOnly', function() local err, _ = file_open(filec, m.kFileWriteOnly, 384) eq(m.UV_ENOENT, err) - local attrs = lfs.attributes(filec) + local attrs = luv.fs_stat(filec) eq(nil, attrs) end) @@ -254,7 +255,7 @@ describe('file_open', function() eq(0, err) eq(true, fp.wr) eq(0, m.file_close(fp, false)) - local attrs = lfs.attributes(file1) + local attrs = luv.fs_stat(file1) eq(0, attrs.size) end) @@ -295,9 +296,9 @@ describe('file_close', function() eq(0, err) local wsize = file_write(fp, 'test') eq(4, wsize) - eq(0, lfs.attributes(filec).size) + eq(0, luv.fs_stat(filec).size) eq(0, m.file_close(fp, true)) - eq(wsize, lfs.attributes(filec).size) + eq(wsize, luv.fs_stat(filec).size) end) end) @@ -307,9 +308,9 @@ describe('file_free', function() eq(0, err) local wsize = file_write(fp, 'test') eq(4, wsize) - eq(0, lfs.attributes(filec).size) + eq(0, luv.fs_stat(filec).size) eq(0, m.file_free(fp, true)) - eq(wsize, lfs.attributes(filec).size) + eq(wsize, luv.fs_stat(filec).size) end) end) @@ -318,12 +319,12 @@ describe('file_fsync', function() local err, fp = file_open(filec, m.kFileCreateOnly, 384) eq(0, file_fsync(fp)) eq(0, err) - eq(0, lfs.attributes(filec).size) + eq(0, luv.fs_stat(filec).size) local wsize = file_write(fp, 'test') eq(4, wsize) - eq(0, lfs.attributes(filec).size) + eq(0, luv.fs_stat(filec).size) eq(0, file_fsync(fp)) - eq(wsize, lfs.attributes(filec).size) + eq(wsize, luv.fs_stat(filec).size) eq(0, m.file_close(fp, false)) end) end) @@ -333,12 +334,12 @@ describe('file_flush', function() local err, fp = file_open(filec, m.kFileCreateOnly, 384) eq(0, file_flush(fp)) eq(0, err) - eq(0, lfs.attributes(filec).size) + eq(0, luv.fs_stat(filec).size) local wsize = file_write(fp, 'test') eq(4, wsize) - eq(0, lfs.attributes(filec).size) + eq(0, luv.fs_stat(filec).size) eq(0, file_flush(fp)) - eq(wsize, lfs.attributes(filec).size) + eq(wsize, luv.fs_stat(filec).size) eq(0, m.file_close(fp, false)) end) end) @@ -412,7 +413,7 @@ describe('file_write', function() local wr = file_write(fp, fcontents) eq(#fcontents, wr) eq(0, m.file_close(fp, false)) - eq(wr, lfs.attributes(filec).size) + eq(wr, luv.fs_stat(filec).size) eq(fcontents, io.open(filec):read('*a')) end) @@ -429,7 +430,7 @@ describe('file_write', function() shift = shift + size end eq(0, m.file_close(fp, false)) - eq(#fcontents, lfs.attributes(filec).size) + eq(#fcontents, luv.fs_stat(filec).size) eq(fcontents, io.open(filec):read('*a')) end) @@ -446,7 +447,7 @@ describe('file_write', function() shift = shift + size end eq(0, m.file_close(fp, false)) - eq(#fcontents, lfs.attributes(filec).size) + eq(#fcontents, luv.fs_stat(filec).size) eq(fcontents, io.open(filec):read('*a')) end) end) diff --git a/test/unit/os/fs_spec.lua b/test/unit/os/fs_spec.lua index c718244ea4..8f45d2b0c7 100644 --- a/test/unit/os/fs_spec.lua +++ b/test/unit/os/fs_spec.lua @@ -1,4 +1,4 @@ -local lfs = require('lfs') +local luv = require('luv') local bit = require('bit') local helpers = require('test.unit.helpers')(after_each) @@ -16,14 +16,12 @@ local to_cstr = helpers.to_cstr local OK = helpers.OK local FAIL = helpers.FAIL local NULL = helpers.NULL +local mkdir = helpers.mkdir +local endswith = helpers.endswith local NODE_NORMAL = 0 local NODE_WRITABLE = 1 -cimport('./src/nvim/os/shell.h') -cimport('./src/nvim/option_defs.h') -cimport('./src/nvim/main.h') -cimport('./src/nvim/fileio.h') local fs = cimport('./src/nvim/os/os.h', './src/nvim/path.h') cppimport('sys/stat.h') cppimport('fcntl.h') @@ -48,11 +46,11 @@ local function unset_bit(number, to_unset) end local function assert_file_exists(filepath) - neq(nil, lfs.attributes(filepath)) + neq(nil, luv.fs_stat(filepath)) end local function assert_file_does_not_exist(filepath) - eq(nil, lfs.attributes(filepath)) + eq(nil, luv.fs_stat(filepath)) end local function os_setperm(filename, perm) @@ -70,14 +68,14 @@ describe('fs.c', function() end before_each(function() - lfs.mkdir('unit-test-directory'); + mkdir('unit-test-directory'); io.open('unit-test-directory/test.file', 'w'):close() io.open('unit-test-directory/test_2.file', 'w'):close() - lfs.link('test.file', 'unit-test-directory/test_link.file', true) + luv.fs_symlink('test.file', 'unit-test-directory/test_link.file') - lfs.link('non_existing_file.file', 'unit-test-directory/test_broken_link.file', true) + luv.fs_symlink('non_existing_file.file', 'unit-test-directory/test_broken_link.file') -- The tests are invoked with an absolute path to `busted` executable. absolute_executable = arg[0] -- Split the absolute_executable path into a directory and filename. @@ -90,19 +88,19 @@ describe('fs.c', function() os.remove('unit-test-directory/test_link.file') os.remove('unit-test-directory/test_hlink.file') os.remove('unit-test-directory/test_broken_link.file') - lfs.rmdir('unit-test-directory') + luv.fs_rmdir('unit-test-directory') end) describe('os_dirname', function() itp('returns OK and writes current directory to the buffer', function() - local length = string.len(lfs.currentdir()) + 1 + local length = string.len(luv.cwd()) + 1 local buf = cstr(length, '') eq(OK, fs.os_dirname(buf, length)) - eq(lfs.currentdir(), ffi.string(buf)) + eq(luv.cwd(), ffi.string(buf)) end) itp('returns FAIL if the buffer is too small', function() - local length = string.len(lfs.currentdir()) + 1 + local length = string.len(luv.cwd()) + 1 local buf = cstr(length - 1, '') eq(FAIL, fs.os_dirname(buf, length - 1)) end) @@ -203,20 +201,20 @@ describe('fs.c', function() end) itp('returns the absolute path when given an executable relative to the current dir', function() - local old_dir = lfs.currentdir() + local old_dir = luv.cwd() - lfs.chdir(directory) + luv.chdir(directory) -- Rely on currentdir to resolve symlinks, if any. Testing against -- the absolute path taken from arg[0] may result in failure where -- the path has a symlink in it. - local canonical = lfs.currentdir() .. '/' .. executable_name + local canonical = luv.cwd() .. '/' .. executable_name local expected = exe(canonical) local relative_executable = './' .. executable_name local res = exe(relative_executable) -- Don't test yet; we need to chdir back first. - lfs.chdir(old_dir) + luv.chdir(old_dir) eq(expected, res) end) end) @@ -278,11 +276,11 @@ describe('fs.c', function() describe('os_fchown', function() local filename = 'unit-test-directory/test.file' itp('does not change owner and group if respective IDs are equal to -1', function() - local uid = lfs.attributes(filename, 'uid') - local gid = lfs.attributes(filename, 'gid') + local uid = luv.fs_stat(filename).uid + local gid = luv.fs_stat(filename).gid eq(0, os_fchown(filename, -1, -1)) - eq(uid, lfs.attributes(filename, 'uid')) - return eq(gid, lfs.attributes(filename, 'gid')) + eq(uid, luv.fs_stat(filename).uid) + return eq(gid, luv.fs_stat(filename).gid) end) -- Some systems may not have `id` utility. @@ -290,7 +288,7 @@ describe('fs.c', function() pending('skipped (missing `id` utility)', function() end) else itp('owner of a file may change the group of the file to any group of which that owner is a member', function() - local file_gid = lfs.attributes(filename, 'gid') + local file_gid = luv.fs_stat(filename).gid -- Gets ID of any group of which current user is a member except the -- group that owns the file. @@ -305,7 +303,7 @@ describe('fs.c', function() -- In that case we can not perform this test. if new_gid then eq(0, (os_fchown(filename, -1, new_gid))) - eq(new_gid, (lfs.attributes(filename, 'gid'))) + eq(new_gid, luv.fs_stat(filename).gid) end end) end @@ -548,7 +546,7 @@ describe('fs.c', function() --create the file local fd = os_open(new_file, ffi.C.kO_CREAT, tonumber("700", 8)) --verify permissions - eq('rwx------', lfs.attributes(new_file)['permissions']) + eq(33216, luv.fs_stat(new_file).mode) eq(0, os_close(fd)) end) @@ -557,7 +555,7 @@ describe('fs.c', function() --create the file local fd = os_open(new_file, ffi.C.kO_CREAT, tonumber("600", 8)) --verify permissions - eq('rw-------', lfs.attributes(new_file)['permissions']) + eq(33152, luv.fs_stat(new_file).mode) eq(0, os_close(fd)) end) @@ -747,12 +745,17 @@ describe('fs.c', function() local function os_mkdir_recurse(path, mode) local failed_str = ffi.new('char *[1]', {nil}) - local ret = fs.os_mkdir_recurse(path, mode, failed_str) - local str = failed_str[0] - if str ~= nil then - str = ffi.string(str) + local created_str = ffi.new('char *[1]', {nil}) + local ret = fs.os_mkdir_recurse(path, mode, failed_str, created_str) + local failed_dir = failed_str[0] + if failed_dir ~= nil then + failed_dir = ffi.string(failed_dir) end - return ret, str + local created_dir = created_str[0] + if created_dir ~= nil then + created_dir = ffi.string(created_dir) + end + return ret, failed_dir, created_dir end describe('os_mkdir', function() @@ -766,81 +769,88 @@ describe('fs.c', function() eq(false, (os_isdir('unit-test-directory/new-dir'))) eq(0, (os_mkdir('unit-test-directory/new-dir', mode))) eq(true, (os_isdir('unit-test-directory/new-dir'))) - lfs.rmdir('unit-test-directory/new-dir') + luv.fs_rmdir('unit-test-directory/new-dir') end) end) describe('os_mkdir_recurse', function() itp('returns zero when given an already existing directory', function() local mode = ffi.C.kS_IRUSR + ffi.C.kS_IWUSR + ffi.C.kS_IXUSR - local ret, failed_str = os_mkdir_recurse('unit-test-directory', mode) + local ret, failed_dir, created_dir = os_mkdir_recurse('unit-test-directory', mode) eq(0, ret) - eq(nil, failed_str) + eq(nil, failed_dir) + eq(nil, created_dir) end) itp('fails to create a directory where there is a file', function() local mode = ffi.C.kS_IRUSR + ffi.C.kS_IWUSR + ffi.C.kS_IXUSR - local ret, failed_str = os_mkdir_recurse( + local ret, failed_dir, created_dir = os_mkdir_recurse( 'unit-test-directory/test.file', mode) neq(0, ret) - eq('unit-test-directory/test.file', failed_str) + eq('unit-test-directory/test.file', failed_dir) + eq(nil, created_dir) end) itp('fails to create a directory where there is a file in path', function() local mode = ffi.C.kS_IRUSR + ffi.C.kS_IWUSR + ffi.C.kS_IXUSR - local ret, failed_str = os_mkdir_recurse( + local ret, failed_dir, created_dir = os_mkdir_recurse( 'unit-test-directory/test.file/test', mode) neq(0, ret) - eq('unit-test-directory/test.file', failed_str) + eq('unit-test-directory/test.file', failed_dir) + eq(nil, created_dir) end) itp('succeeds to create a directory', function() local mode = ffi.C.kS_IRUSR + ffi.C.kS_IWUSR + ffi.C.kS_IXUSR - local ret, failed_str = os_mkdir_recurse( + local ret, failed_dir, created_dir = os_mkdir_recurse( 'unit-test-directory/new-dir-recurse', mode) eq(0, ret) - eq(nil, failed_str) + eq(nil, failed_dir) + ok(endswith(created_dir, 'unit-test-directory/new-dir-recurse')) eq(true, os_isdir('unit-test-directory/new-dir-recurse')) - lfs.rmdir('unit-test-directory/new-dir-recurse') + luv.fs_rmdir('unit-test-directory/new-dir-recurse') eq(false, os_isdir('unit-test-directory/new-dir-recurse')) end) itp('succeeds to create a directory ending with ///', function() local mode = ffi.C.kS_IRUSR + ffi.C.kS_IWUSR + ffi.C.kS_IXUSR - local ret, failed_str = os_mkdir_recurse( + local ret, failed_dir, created_dir = os_mkdir_recurse( 'unit-test-directory/new-dir-recurse///', mode) eq(0, ret) - eq(nil, failed_str) + eq(nil, failed_dir) + ok(endswith(created_dir, 'unit-test-directory/new-dir-recurse')) eq(true, os_isdir('unit-test-directory/new-dir-recurse')) - lfs.rmdir('unit-test-directory/new-dir-recurse') + luv.fs_rmdir('unit-test-directory/new-dir-recurse') eq(false, os_isdir('unit-test-directory/new-dir-recurse')) end) itp('succeeds to create a directory ending with /', function() local mode = ffi.C.kS_IRUSR + ffi.C.kS_IWUSR + ffi.C.kS_IXUSR - local ret, failed_str = os_mkdir_recurse( + local ret, failed_dir, created_dir = os_mkdir_recurse( 'unit-test-directory/new-dir-recurse/', mode) eq(0, ret) - eq(nil, failed_str) + eq(nil, failed_dir) + ok(endswith(created_dir, 'unit-test-directory/new-dir-recurse')) eq(true, os_isdir('unit-test-directory/new-dir-recurse')) - lfs.rmdir('unit-test-directory/new-dir-recurse') + luv.fs_rmdir('unit-test-directory/new-dir-recurse') eq(false, os_isdir('unit-test-directory/new-dir-recurse')) end) itp('succeeds to create a directory tree', function() local mode = ffi.C.kS_IRUSR + ffi.C.kS_IWUSR + ffi.C.kS_IXUSR - local ret, failed_str = os_mkdir_recurse( + local ret, failed_dir, created_dir = os_mkdir_recurse( 'unit-test-directory/new-dir-recurse/1/2/3', mode) eq(0, ret) - eq(nil, failed_str) + eq(nil, failed_dir) + ok(endswith(created_dir, 'unit-test-directory/new-dir-recurse')) eq(true, os_isdir('unit-test-directory/new-dir-recurse')) eq(true, os_isdir('unit-test-directory/new-dir-recurse/1')) eq(true, os_isdir('unit-test-directory/new-dir-recurse/1/2')) eq(true, os_isdir('unit-test-directory/new-dir-recurse/1/2/3')) - lfs.rmdir('unit-test-directory/new-dir-recurse/1/2/3') - lfs.rmdir('unit-test-directory/new-dir-recurse/1/2') - lfs.rmdir('unit-test-directory/new-dir-recurse/1') - lfs.rmdir('unit-test-directory/new-dir-recurse') + luv.fs_rmdir('unit-test-directory/new-dir-recurse/1/2/3') + luv.fs_rmdir('unit-test-directory/new-dir-recurse/1/2') + luv.fs_rmdir('unit-test-directory/new-dir-recurse/1') + luv.fs_rmdir('unit-test-directory/new-dir-recurse') eq(false, os_isdir('unit-test-directory/new-dir-recurse')) end) end) @@ -851,7 +861,7 @@ describe('fs.c', function() end) itp('removes the given directory and returns 0', function() - lfs.mkdir('unit-test-directory/new-dir') + mkdir('unit-test-directory/new-dir') eq(0, os_rmdir('unit-test-directory/new-dir')) eq(false, (os_isdir('unit-test-directory/new-dir'))) end) @@ -1005,7 +1015,7 @@ describe('fs.c', function() file:write('some bytes to get filesize != 0') file:flush() file:close() - local size = lfs.attributes(path, 'size') + local size = luv.fs_stat(path).size local info = file_info_new() assert.is_true(fs.os_fileinfo(path, info)) eq(size, fs.os_fileinfo_size(info)) @@ -1019,7 +1029,7 @@ describe('fs.c', function() local info = file_info_new() assert.is_true(fs.os_fileinfo(path, info)) eq(1, fs.os_fileinfo_hardlinks(info)) - lfs.link(path, path_link) + luv.fs_link(path, path_link) assert.is_true(fs.os_fileinfo(path, info)) eq(2, fs.os_fileinfo_hardlinks(info)) end) @@ -1028,11 +1038,7 @@ describe('fs.c', function() describe('os_fileinfo_blocksize', function() itp('returns the correct blocksize of a file', function() local path = 'unit-test-directory/test.file' - -- there is a bug in luafilesystem where - -- `lfs.attributes path, 'blksize'` returns the wrong value: - -- https://github.com/keplerproject/luafilesystem/pull/44 - -- using this workaround for now: - local blksize = lfs.attributes(path).blksize + local blksize = luv.fs_stat(path).blksize local info = file_info_new() assert.is_true(fs.os_fileinfo(path, info)) if blksize then diff --git a/test/unit/os/shell_spec.lua b/test/unit/os/shell_spec.lua index 29a2b78491..3fb1afed44 100644 --- a/test/unit/os/shell_spec.lua +++ b/test/unit/os/shell_spec.lua @@ -2,7 +2,7 @@ local helpers = require('test.unit.helpers')(after_each) local itp = helpers.gen_itp(it) local cimported = helpers.cimport( './src/nvim/os/shell.h', - './src/nvim/option_defs.h', + './src/nvim/option_vars.h', './src/nvim/main.h', './src/nvim/memory.h' ) diff --git a/test/unit/path_spec.lua b/test/unit/path_spec.lua index 1fc4e2496e..23f71cfe78 100644 --- a/test/unit/path_spec.lua +++ b/test/unit/path_spec.lua @@ -1,4 +1,4 @@ -local lfs = require('lfs') +local luv = require('luv') local helpers = require('test.unit.helpers')(after_each) local itp = helpers.gen_itp(it) @@ -11,9 +11,11 @@ local to_cstr = helpers.to_cstr local NULL = helpers.NULL local OK = helpers.OK local FAIL = helpers.FAIL +local mkdir = helpers.mkdir cimport('string.h') local cimp = cimport('./src/nvim/os/os.h', './src/nvim/path.h') +local options = cimport('./src/nvim/option_vars.h') local length = 0 local buffer = nil @@ -21,11 +23,11 @@ local buffer = nil describe('path.c', function() describe('path_full_dir_name', function() setup(function() - lfs.mkdir('unit-test-directory') + mkdir('unit-test-directory') end) teardown(function() - lfs.rmdir('unit-test-directory') + luv.fs_rmdir('unit-test-directory') end) local function path_full_dir_name(directory, buf, len) @@ -35,34 +37,34 @@ describe('path.c', function() before_each(function() -- Create empty string buffer which will contain the resulting path. - length = string.len(lfs.currentdir()) + 22 + length = string.len(luv.cwd()) + 22 buffer = cstr(length, '') end) itp('returns the absolute directory name of a given relative one', function() local result = path_full_dir_name('..', buffer, length) eq(OK, result) - local old_dir = lfs.currentdir() - lfs.chdir('..') - local expected = lfs.currentdir() - lfs.chdir(old_dir) + local old_dir = luv.cwd() + luv.chdir('..') + local expected = luv.cwd() + luv.chdir(old_dir) eq(expected, (ffi.string(buffer))) end) itp('returns the current directory name if the given string is empty', function() eq(OK, (path_full_dir_name('', buffer, length))) - eq(lfs.currentdir(), (ffi.string(buffer))) + eq(luv.cwd(), (ffi.string(buffer))) end) itp('works with a normal relative dir', function() local result = path_full_dir_name('unit-test-directory', buffer, length) - eq(lfs.currentdir() .. '/unit-test-directory', (ffi.string(buffer))) + eq(luv.cwd() .. '/unit-test-directory', (ffi.string(buffer))) eq(OK, result) end) itp('works with a non-existing relative dir', function() local result = path_full_dir_name('does-not-exist', buffer, length) - eq(lfs.currentdir() .. '/does-not-exist', (ffi.string(buffer))) + eq(luv.cwd() .. '/does-not-exist', (ffi.string(buffer))) eq(OK, result) end) @@ -268,27 +270,27 @@ describe('path.c', function() end) describe('path_try_shorten_fname', function() - local cwd = lfs.currentdir() + local cwd = luv.cwd() before_each(function() - lfs.mkdir('ut_directory') + mkdir('ut_directory') end) after_each(function() - lfs.chdir(cwd) - lfs.rmdir('ut_directory') + luv.chdir(cwd) + luv.fs_rmdir('ut_directory') end) describe('path_try_shorten_fname', function() itp('returns shortened path if possible', function() - lfs.chdir('ut_directory') - local full = to_cstr(lfs.currentdir() .. '/subdir/file.txt') + luv.chdir('ut_directory') + local full = to_cstr(luv.cwd() .. '/subdir/file.txt') eq('subdir/file.txt', (ffi.string(cimp.path_try_shorten_fname(full)))) end) itp('returns `full_path` if a shorter version is not possible', function() - local old = lfs.currentdir() - lfs.chdir('ut_directory') + local old = luv.cwd() + luv.chdir('ut_directory') local full = old .. '/subdir/file.txt' eq(full, (ffi.string(cimp.path_try_shorten_fname(to_cstr(full))))) end) @@ -300,7 +302,7 @@ describe('path_try_shorten_fname', function() end) describe('path.c path_guess_exepath', function() - local cwd = lfs.currentdir() + local cwd = luv.cwd() for _,name in ipairs({'./nvim', '.nvim', 'foo/nvim'}) do itp('"'..name..'" returns name catenated with CWD', function() @@ -354,7 +356,7 @@ end) describe('path.c', function() setup(function() - lfs.mkdir('unit-test-directory'); + mkdir('unit-test-directory'); io.open('unit-test-directory/test.file', 'w'):close() -- Since the tests are executed, they are called by an executable. We use @@ -368,7 +370,7 @@ describe('path.c', function() teardown(function() os.remove('unit-test-directory/test.file') - lfs.rmdir('unit-test-directory') + luv.fs_rmdir('unit-test-directory') end) describe('vim_FullName', function() @@ -420,7 +422,7 @@ describe('path.c', function() end) itp('concatenates filename if it does not contain a slash', function() - local expected = lfs.currentdir() .. '/test.file' + local expected = luv.cwd() .. '/test.file' local filename = 'test.file' local buflen = get_buf_len(expected, filename) local do_expand = 1 @@ -430,7 +432,7 @@ describe('path.c', function() end) itp('concatenates directory name if it does not contain a slash', function() - local expected = lfs.currentdir() .. '/..' + local expected = luv.cwd() .. '/..' local filename = '..' local buflen = get_buf_len(expected, filename) local do_expand = 1 @@ -440,10 +442,10 @@ describe('path.c', function() end) itp('enters given directory (instead of just concatenating the strings) if possible and if path contains a slash', function() - local old_dir = lfs.currentdir() - lfs.chdir('..') - local expected = lfs.currentdir() .. '/test.file' - lfs.chdir(old_dir) + local old_dir = luv.cwd() + luv.chdir('..') + local expected = luv.cwd() .. '/test.file' + luv.chdir(old_dir) local filename = '../test.file' local buflen = get_buf_len(expected, filename) local do_expand = 1 @@ -472,7 +474,7 @@ describe('path.c', function() end) itp('works with some "normal" relative path with directories', function() - local expected = lfs.currentdir() .. '/unit-test-directory/test.file' + local expected = luv.cwd() .. '/unit-test-directory/test.file' local filename = 'unit-test-directory/test.file' local buflen = get_buf_len(expected, filename) local do_expand = 1 @@ -482,7 +484,7 @@ describe('path.c', function() end) itp('does not modify the given filename', function() - local expected = lfs.currentdir() .. '/unit-test-directory/test.file' + local expected = luv.cwd() .. '/unit-test-directory/test.file' local filename = to_cstr('unit-test-directory/test.file') local buflen = string.len(expected) + 1 local buf = cstr(buflen, '') @@ -505,7 +507,7 @@ describe('path.c', function() end) itp('does not remove trailing slash from non-existing relative directory #20847', function() - local expected = lfs.currentdir() .. '/non_existing_dir/' + local expected = luv.cwd() .. '/non_existing_dir/' local filename = 'non_existing_dir/' local buflen = get_buf_len(expected, filename) local do_expand = 1 @@ -515,7 +517,7 @@ describe('path.c', function() end) itp('expands "./" to the current directory #7117', function() - local expected = lfs.currentdir() .. '/unit-test-directory/test.file' + local expected = luv.cwd() .. '/unit-test-directory/test.file' local filename = './unit-test-directory/test.file' local buflen = get_buf_len(expected, filename) local do_expand = 1 @@ -525,7 +527,7 @@ describe('path.c', function() end) itp('collapses "foo/../foo" to "foo" #7117', function() - local expected = lfs.currentdir() .. '/unit-test-directory/test.file' + local expected = luv.cwd() .. '/unit-test-directory/test.file' local filename = 'unit-test-directory/../unit-test-directory/test.file' local buflen = get_buf_len(expected, filename) local do_expand = 1 @@ -542,8 +544,8 @@ describe('path.c', function() return ffi.string(c_file) end - before_each(function() lfs.mkdir('CamelCase') end) - after_each(function() lfs.rmdir('CamelCase') end) + before_each(function() mkdir('CamelCase') end) + after_each(function() luv.fs_rmdir('CamelCase') end) if ffi.os == 'Windows' or ffi.os == 'OSX' then itp('Corrects the case of file names in Mac and Windows', function() @@ -635,6 +637,15 @@ describe('path.c', function() eq(false, path_with_extension('/some/path/file.vim', 'lua')) eq(false, path_with_extension('/some/path/file', 'lua')) end) + + itp("respects 'fileignorecase' option", function() + options.p_fic = false + eq(false, path_with_extension('/some/path/file.VIM', 'vim')) + eq(false, path_with_extension('/some/path/file.LUA', 'lua')) + options.p_fic = true + eq(true, path_with_extension('/some/path/file.VIM', 'vim')) + eq(true, path_with_extension('/some/path/file.LUA', 'lua')) + end) end) describe('path_with_url', function() diff --git a/test/unit/preload.lua b/test/unit/preload.lua index 841e19b878..c2d051d98a 100644 --- a/test/unit/preload.lua +++ b/test/unit/preload.lua @@ -3,5 +3,4 @@ -- for more information about this. local ffi = require('ffi') local helpers = require('test.unit.helpers')(nil) -local lfs = require('lfs') local preprocess = require('test.unit.preprocess') diff --git a/test/unit/preprocess.lua b/test/unit/preprocess.lua index 1073855a7d..e356695c14 100644 --- a/test/unit/preprocess.lua +++ b/test/unit/preprocess.lua @@ -7,6 +7,9 @@ local global_helpers = require('test.helpers') local argss_to_cmd = global_helpers.argss_to_cmd local repeated_read_cmd = global_helpers.repeated_read_cmd +--- @alias Compiler {path: string[], type: string} + +--- @type Compiler[] local ccs = {} local env_cc = os.getenv("CC") @@ -27,6 +30,8 @@ table.insert(ccs, {path = {"/usr/bin/env", "clang"}, type = "clang"}) table.insert(ccs, {path = {"/usr/bin/env", "icc"}, type = "gcc"}) -- parse Makefile format dependencies into a Lua table +--- @param deps string +--- @return string[] local function parse_make_deps(deps) -- remove line breaks and line concatenators deps = deps:gsub("\n", ""):gsub("\\", "") @@ -36,7 +41,7 @@ local function parse_make_deps(deps) deps = deps:gsub(" +", " ") -- split according to token (space in this case) - local headers = {} + local headers = {} --- @type string[] for token in deps:gmatch("[^%s]+") do -- headers[token] = true headers[#headers + 1] = token @@ -53,57 +58,58 @@ local function parse_make_deps(deps) return headers end --- will produce a string that represents a meta C header file that includes --- all the passed in headers. I.e.: --- --- headerize({"stdio.h", "math.h"}, true) --- produces: --- #include <stdio.h> --- #include <math.h> --- --- headerize({"vim.h", "memory.h"}, false) --- produces: --- #include "vim.h" --- #include "memory.h" +--- will produce a string that represents a meta C header file that includes +--- all the passed in headers. I.e.: +--- +--- headerize({"stdio.h", "math.h"}, true) +--- produces: +--- #include <stdio.h> +--- #include <math.h> +--- +--- headerize({"vim_defs.h", "memory.h"}, false) +--- produces: +--- #include "vim_defs.h" +--- #include "memory.h" +--- @param headers string[] +--- @param global? boolean +--- @return string local function headerize(headers, global) - local pre = '"' - local post = pre - if global then - pre = "<" - post = ">" - end - - local formatted = {} + local fmt = global and '#include <%s>' or '#include "%s"' + local formatted = {} --- @type string[] for _, hdr in ipairs(headers) do - formatted[#formatted + 1] = "#include " .. - tostring(pre) .. - tostring(hdr) .. - tostring(post) + formatted[#formatted + 1] = string.format(fmt, hdr) end return table.concat(formatted, "\n") end +--- @class Gcc +--- @field path string +--- @field preprocessor_extra_flags string[] +--- @field get_defines_extra_flags string[] +--- @field get_declarations_extra_flags string[] local Gcc = { preprocessor_extra_flags = {}, get_defines_extra_flags = {'-std=c99', '-dM', '-E'}, get_declarations_extra_flags = {'-std=c99', '-P', '-E'}, } +--- @param name string +--- @param args string[]? +--- @param val string? function Gcc:define(name, args, val) - local define = '-D' .. name - if args ~= nil then - define = define .. '(' .. table.concat(args, ',') .. ')' + local define = string.format('-D%s', name) + if args then + define = string.format('%s(%s)', define, table.concat(args, ',')) end - if val ~= nil then - define = define .. '=' .. val + if val then + define = string.format('%s=%s', define, val) end self.preprocessor_extra_flags[#self.preprocessor_extra_flags + 1] = define end function Gcc:undefine(name) - self.preprocessor_extra_flags[#self.preprocessor_extra_flags + 1] = ( - '-U' .. name) + self.preprocessor_extra_flags[#self.preprocessor_extra_flags + 1] = '-U' .. name end function Gcc:init_defines() @@ -128,6 +134,8 @@ function Gcc:init_defines() self:undefine('__BLOCKS__') end +--- @param obj? Compiler +--- @return Gcc function Gcc:new(obj) obj = obj or {} setmetatable(obj, self) @@ -136,6 +144,7 @@ function Gcc:new(obj) return obj end +--- @param ... string function Gcc:add_to_include_path(...) for i = 1, select('#', ...) do local path = select(i, ...) @@ -145,116 +154,115 @@ function Gcc:add_to_include_path(...) end -- returns a list of the headers files upon which this file relies +--- @param hdr string +--- @return string[]? function Gcc:dependencies(hdr) + --- @type string local cmd = argss_to_cmd(self.path, {'-M', hdr}) .. ' 2>&1' - local out = io.popen(cmd) + local out = assert(io.popen(cmd)) local deps = out:read("*a") out:close() if deps then return parse_make_deps(deps) - else - return nil end end +--- @param defines string +--- @return string function Gcc:filter_standard_defines(defines) if not self.standard_defines then local pseudoheader_fname = 'tmp_empty_pseudoheader.h' - local pseudoheader_file = io.open(pseudoheader_fname, 'w') + local pseudoheader_file = assert(io.open(pseudoheader_fname, 'w')) pseudoheader_file:close() - local standard_defines = repeated_read_cmd(self.path, - self.preprocessor_extra_flags, - self.get_defines_extra_flags, - {pseudoheader_fname}) + local standard_defines = assert(repeated_read_cmd(self.path, + self.preprocessor_extra_flags, + self.get_defines_extra_flags, + {pseudoheader_fname})) os.remove(pseudoheader_fname) - self.standard_defines = {} + self.standard_defines = {} --- @type table<string,true> for line in standard_defines:gmatch('[^\n]+') do self.standard_defines[line] = true end end - local ret = {} + + local ret = {} --- @type string[] for line in defines:gmatch('[^\n]+') do if not self.standard_defines[line] then ret[#ret + 1] = line end end + return table.concat(ret, "\n") end --- returns a stream representing a preprocessed form of the passed-in headers. --- Don't forget to close the stream by calling the close() method on it. +--- returns a stream representing a preprocessed form of the passed-in headers. +--- Don't forget to close the stream by calling the close() method on it. +--- @param previous_defines string +--- @param ... string +--- @return string, string function Gcc:preprocess(previous_defines, ...) -- create pseudo-header local pseudoheader = headerize({...}, false) local pseudoheader_fname = 'tmp_pseudoheader.h' - local pseudoheader_file = io.open(pseudoheader_fname, 'w') + local pseudoheader_file = assert(io.open(pseudoheader_fname, 'w')) pseudoheader_file:write(previous_defines) pseudoheader_file:write("\n") pseudoheader_file:write(pseudoheader) pseudoheader_file:flush() pseudoheader_file:close() - local defines = repeated_read_cmd(self.path, self.preprocessor_extra_flags, - self.get_defines_extra_flags, - {pseudoheader_fname}) + local defines = assert(repeated_read_cmd(self.path, self.preprocessor_extra_flags, + self.get_defines_extra_flags, + {pseudoheader_fname})) defines = self:filter_standard_defines(defines) - -- lfs = require("lfs") - -- print("CWD: #{lfs.currentdir!}") - -- print("CMD: #{cmd}") - -- io.stderr\write("CWD: #{lfs.currentdir!}\n") - -- io.stderr\write("CMD: #{cmd}\n") - - local declarations = repeated_read_cmd(self.path, - self.preprocessor_extra_flags, - self.get_declarations_extra_flags, - {pseudoheader_fname}) + local declarations = assert(repeated_read_cmd(self.path, + self.preprocessor_extra_flags, + self.get_declarations_extra_flags, + {pseudoheader_fname})) os.remove(pseudoheader_fname) - assert(declarations and defines) return declarations, defines end -local Clang = Gcc:new() -local Msvc = Gcc:new() - -local type_to_class = { - ["gcc"] = Gcc, - ["clang"] = Clang, - ["msvc"] = Msvc -} - -- 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 +--- @param compilers Compiler[] +--- @return Gcc? local function find_best_cc(compilers) for _, meta in pairs(compilers) do - local version = io.popen(tostring(meta.path) .. " -v 2>&1") + local version = assert(io.popen(tostring(meta.path) .. " -v 2>&1")) version:close() if version then - return type_to_class[meta.type]:new({path = meta.path}) + return Gcc:new({path = meta.path}) end end - return nil end -- 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 cc = nil -if cc == nil then - cc = find_best_cc(ccs) +local cc = assert(find_best_cc(ccs)) + +local M = {} + +--- @param hdr string +--- @return string[]? +function M.includes(hdr) + return cc:dependencies(hdr) end -return { - includes = function(hdr) - return cc:dependencies(hdr) - end, - preprocess = function(...) - return cc:preprocess(...) - end, - add_to_include_path = function(...) - return cc:add_to_include_path(...) - end -} +--- @param ... string +--- @return string, string +function M.preprocess(...) + return cc:preprocess(...) +end + +--- @param ... string +function M.add_to_include_path(...) + return cc:add_to_include_path(...) +end + +return M diff --git a/test/unit/set.lua b/test/unit/set.lua index f3d68c3042..7c30be32aa 100644 --- a/test/unit/set.lua +++ b/test/unit/set.lua @@ -4,10 +4,15 @@ -- other: -- 1) index => item -- 2) item => index +--- @class Set +--- @field nelem integer +--- @field items string[] +--- @field tbl table local Set = {} +--- @param items? string[] function Set:new(items) - local obj = {} + local obj = {} --- @type Set setmetatable(obj, self) self.__index = self @@ -26,11 +31,9 @@ function Set:new(items) return obj end +--- @return Set function Set:copy() - local obj = {} - obj.nelem = self.nelem - obj.tbl = {} - obj.items = {} + local obj = {nelem = self.nelem, tbl = {}, items = {}} --- @type Set for k, v in pairs(self.tbl) do obj.tbl[k] = v end @@ -43,6 +46,7 @@ function Set:copy() end -- adds the argument Set to this Set +--- @param other Set function Set:union(other) for e in other:iterator() do self:add(e) @@ -57,6 +61,7 @@ function Set:union_table(t) end -- subtracts the argument Set from this Set +--- @param other Set function Set:diff(other) if other:size() > self:size() then -- this set is smaller than the other set @@ -75,6 +80,7 @@ function Set:diff(other) end end +--- @param it string function Set:add(it) if not self:contains(it) then local idx = #self.tbl + 1 @@ -84,6 +90,7 @@ function Set:add(it) end end +--- @param it string function Set:remove(it) if self:contains(it) then local idx = self.items[it] @@ -93,10 +100,13 @@ function Set:remove(it) end end +--- @param it string +--- @return boolean function Set:contains(it) return self.items[it] or false end +--- @return integer function Set:size() return self.nelem end @@ -113,29 +123,18 @@ function Set:iterator() return pairs(self.items) end +--- @return string[] function Set:to_table() -- there might be gaps in @tbl, so we have to be careful and sort first - local keys - do - local _accum_0 = { } - local _len_0 = 1 - for idx, _ in pairs(self.tbl) do - _accum_0[_len_0] = idx - _len_0 = _len_0 + 1 - end - keys = _accum_0 + local keys = {} --- @type string[] + for idx, _ in pairs(self.tbl) do + keys[#keys+1] = idx end + table.sort(keys) - local copy - do - local _accum_0 = { } - local _len_0 = 1 - for _index_0 = 1, #keys do - local idx = keys[_index_0] - _accum_0[_len_0] = self.tbl[idx] - _len_0 = _len_0 + 1 - end - copy = _accum_0 + local copy = {} --- @type string[] + for _, idx in ipairs(keys) do + copy[#copy+1] = self.tbl[idx] end return copy end diff --git a/test/unit/statusline_spec.lua b/test/unit/statusline_spec.lua new file mode 100644 index 0000000000..a124a588e9 --- /dev/null +++ b/test/unit/statusline_spec.lua @@ -0,0 +1,282 @@ +local helpers = require("test.unit.helpers")(after_each) +local itp = helpers.gen_itp(it) + +local to_cstr = helpers.to_cstr +local get_str = helpers.ffi.string +local eq = helpers.eq +local NULL = helpers.NULL + +local buffer = helpers.cimport("./src/nvim/buffer.h") +local globals = helpers.cimport("./src/nvim/globals.h") +local stl = helpers.cimport("./src/nvim/statusline.h") + +describe('build_stl_str_hl', function() + local buffer_byte_size = 100 + local STL_INITIAL_ITEMS = 20 + local output_buffer = '' + + -- This function builds the statusline + -- + -- @param arg Optional arguments are: + -- .pat The statusline format string + -- .fillchar The fill character used in the statusline + -- .maximum_cell_count The number of cells available in the statusline + local function build_stl_str_hl(arg) + output_buffer = to_cstr(string.rep(" ", buffer_byte_size)) + + local pat = arg.pat or '' + local fillchar = arg.fillchar or (' '):byte() + local maximum_cell_count = arg.maximum_cell_count or buffer_byte_size + + return stl.build_stl_str_hl(globals.curwin, + output_buffer, + buffer_byte_size, + to_cstr(pat), + NULL, + 0, + fillchar, + maximum_cell_count, + NULL, + NULL, + NULL) + end + + -- Use this function to simplify testing the comparison between + -- the format string and the resulting statusline. + -- + -- @param description The description of what the test should be doing + -- @param statusline_cell_count The number of cells available in the statusline + -- @param input_stl The format string for the statusline + -- @param expected_stl The expected result string for the statusline + -- + -- @param arg Options can be placed in an optional dictionary as the last parameter + -- .expected_cell_count The expected number of cells build_stl_str_hl will return + -- .expected_byte_length The expected byte length of the string (defaults to byte length of expected_stl) + -- .file_name The name of the file to be tested (useful in %f type tests) + -- .fillchar The character that will be used to fill any 'extra' space in the stl + local function statusline_test(description, + statusline_cell_count, + input_stl, + expected_stl, + arg) + + -- arg is the optional parameter + -- so we either fill in option with arg or an empty dictionary + local option = arg or {} + + local fillchar = option.fillchar or (' '):byte() + local expected_cell_count = option.expected_cell_count or statusline_cell_count + local expected_byte_length = option.expected_byte_length or #expected_stl + + itp(description, function() + if option.file_name then + buffer.setfname(globals.curbuf, to_cstr(option.file_name), NULL, 1) + else + buffer.setfname(globals.curbuf, nil, NULL, 1) + end + + local result_cell_count = build_stl_str_hl{pat=input_stl, + maximum_cell_count=statusline_cell_count, + fillchar=fillchar} + + eq(expected_stl, get_str(output_buffer, expected_byte_length)) + eq(expected_cell_count, result_cell_count) + end) + end + + -- expression testing + statusline_test('Should expand expression', 2, + '%!expand(20+1)', '21') + statusline_test('Should expand broken expression to itself', 11, + '%!expand(20+1', 'expand(20+1') + + -- file name testing + statusline_test('should print no file name', 10, + '%f', '[No Name]', + {expected_cell_count=9}) + statusline_test('should print the relative file name', 30, + '%f', 'test/unit/buffer_spec.lua', + {file_name='test/unit/buffer_spec.lua', expected_cell_count=25}) + statusline_test('should print the full file name', 40, + '%F', '/test/unit/buffer_spec.lua', + {file_name='/test/unit/buffer_spec.lua', expected_cell_count=26}) + + -- fillchar testing + statusline_test('should handle `!` as a fillchar', 10, + 'abcde%=', 'abcde!!!!!', + {fillchar=('!'):byte()}) + statusline_test('should handle `~` as a fillchar', 10, + '%=abcde', '~~~~~abcde', + {fillchar=('~'):byte()}) + statusline_test('should put fillchar `!` in between text', 10, + 'abc%=def', 'abc!!!!def', + {fillchar=('!'):byte()}) + statusline_test('should put fillchar `~` in between text', 10, + 'abc%=def', 'abc~~~~def', + {fillchar=('~'):byte()}) + statusline_test('should put fillchar `━` in between text', 10, + 'abc%=def', 'abc━━━━def', + {fillchar=0x2501}) + statusline_test('should handle zero-fillchar as a space', 10, + 'abcde%=', 'abcde ', + {fillchar=0}) + statusline_test('should print the tail file name', 80, + '%t', 'buffer_spec.lua', + {file_name='test/unit/buffer_spec.lua', expected_cell_count=15}) + + -- standard text testing + statusline_test('should copy plain text', 80, + 'this is a test', 'this is a test', + {expected_cell_count=14}) + + -- line number testing + statusline_test('should print the buffer number', 80, + '%n', '1', + {expected_cell_count=1}) + statusline_test('should print the current line number in the buffer', 80, + '%l', '0', + {expected_cell_count=1}) + statusline_test('should print the number of lines in the buffer', 80, + '%L', '1', + {expected_cell_count=1}) + + -- truncation testing + statusline_test('should truncate when standard text pattern is too long', 10, + '0123456789abcde', '<6789abcde') + statusline_test('should truncate when using =', 10, + 'abcdef%=ghijkl', 'abcdef<jkl') + statusline_test('should truncate centered text when using ==', 10, + 'abcde%=gone%=fghij', 'abcde<ghij') + statusline_test('should respect the `<` marker', 10, + 'abc%<defghijkl', 'abc<ghijkl') + statusline_test('should truncate at `<` with one `=`, test 1', 10, + 'abc%<def%=ghijklmno', 'abc<jklmno') + statusline_test('should truncate at `<` with one `=`, test 2', 10, + 'abcdef%=ghijkl%<mno', 'abcdefghi>') + statusline_test('should truncate at `<` with one `=`, test 3', 10, + 'abc%<def%=ghijklmno', 'abc<jklmno') + statusline_test('should truncate at `<` with one `=`, test 4', 10, + 'abc%<def%=ghij', 'abcdefghij') + statusline_test('should truncate at `<` with one `=`, test 4', 10, + 'abc%<def%=ghijk', 'abc<fghijk') + + statusline_test('should truncate at `<` with many `=`, test 4', 10, + 'ab%<cdef%=g%=h%=ijk', 'ab<efghijk') + + statusline_test('should truncate at the first `<`', 10, + 'abc%<def%<ghijklm', 'abc<hijklm') + + statusline_test('should ignore trailing %', 3, 'abc%', 'abc') + + -- alignment testing with fillchar + local function statusline_test_align(description, + statusline_cell_count, + input_stl, + expected_stl, + arg) + arg = arg or {} + statusline_test(description .. ' without fillchar', + statusline_cell_count, input_stl, expected_stl:gsub('%~', ' '), arg) + arg.fillchar = ('!'):byte() + statusline_test(description .. ' with fillchar `!`', + statusline_cell_count, input_stl, expected_stl:gsub('%~', '!'), arg) + arg.fillchar = 0x2501 + statusline_test(description .. ' with fillchar `━`', + statusline_cell_count, input_stl, expected_stl:gsub('%~', '━'), arg) + end + + statusline_test_align('should right align when using =', 20, + 'neo%=vim', 'neo~~~~~~~~~~~~~~vim') + statusline_test_align('should, when possible, center text when using %=text%=', 20, + 'abc%=neovim%=def', 'abc~~~~neovim~~~~def') + statusline_test_align('should handle uneven spacing in the buffer when using %=text%=', 20, + 'abc%=neo_vim%=def', 'abc~~~neo_vim~~~~def') + statusline_test_align('should have equal spaces even with non-equal sides when using =', 20, + 'foobar%=test%=baz', 'foobar~~~test~~~~baz') + statusline_test_align('should have equal spaces even with longer right side when using =', 20, + 'a%=test%=longtext', 'a~~~test~~~~longtext') + statusline_test_align('should handle an empty left side when using ==', 20, + '%=test%=baz', '~~~~~~test~~~~~~~baz') + statusline_test_align('should handle an empty right side when using ==', 20, + 'foobar%=test%=', 'foobar~~~~~test~~~~~') + statusline_test_align('should handle consecutive empty ==', 20, + '%=%=test%=', '~~~~~~~~~~test~~~~~~') + statusline_test_align('should handle an = alone', 20, + '%=', '~~~~~~~~~~~~~~~~~~~~') + statusline_test_align('should right align text when it is alone with =', 20, + '%=foo', '~~~~~~~~~~~~~~~~~foo') + statusline_test_align('should left align text when it is alone with =', 20, + 'foo%=', 'foo~~~~~~~~~~~~~~~~~') + + statusline_test_align('should approximately center text when using %=text%=', 21, + 'abc%=neovim%=def', 'abc~~~~neovim~~~~~def') + statusline_test_align('should completely fill the buffer when using %=text%=', 21, + 'abc%=neo_vim%=def', 'abc~~~~neo_vim~~~~def') + statusline_test_align('should have equal spacing even with non-equal sides when using =', 21, + 'foobar%=test%=baz', 'foobar~~~~test~~~~baz') + statusline_test_align('should have equal spacing even with longer right side when using =', 21, + 'a%=test%=longtext', 'a~~~~test~~~~longtext') + statusline_test_align('should handle an empty left side when using ==', 21, + '%=test%=baz', '~~~~~~~test~~~~~~~baz') + statusline_test_align('should handle an empty right side when using ==', 21, + 'foobar%=test%=', 'foobar~~~~~test~~~~~~') + + statusline_test_align('should quadrant the text when using 3 %=', 40, + 'abcd%=n%=eovim%=ef', 'abcd~~~~~~~~~n~~~~~~~~~eovim~~~~~~~~~~ef') + statusline_test_align('should work well with %t', 40, + '%t%=right_aligned', 'buffer_spec.lua~~~~~~~~~~~~right_aligned', + {file_name='test/unit/buffer_spec.lua'}) + statusline_test_align('should work well with %t and regular text', 40, + 'l%=m_l %t m_r%=r', 'l~~~~~~~m_l buffer_spec.lua m_r~~~~~~~~r', + {file_name='test/unit/buffer_spec.lua'}) + statusline_test_align('should work well with %=, %t, %L, and %l', 40, + '%t %= %L %= %l', 'buffer_spec.lua ~~~~~~~~~ 1 ~~~~~~~~~~ 0', + {file_name='test/unit/buffer_spec.lua'}) + + statusline_test_align('should quadrant the text when using 3 %=', 41, + 'abcd%=n%=eovim%=ef', 'abcd~~~~~~~~~n~~~~~~~~~eovim~~~~~~~~~~~ef') + statusline_test_align('should work well with %t', 41, + '%t%=right_aligned', 'buffer_spec.lua~~~~~~~~~~~~~right_aligned', + {file_name='test/unit/buffer_spec.lua'}) + statusline_test_align('should work well with %t and regular text', 41, + 'l%=m_l %t m_r%=r', 'l~~~~~~~~m_l buffer_spec.lua m_r~~~~~~~~r', + {file_name='test/unit/buffer_spec.lua'}) + statusline_test_align('should work well with %=, %t, %L, and %l', 41, + '%t %= %L %= %l', 'buffer_spec.lua ~~~~~~~~~~ 1 ~~~~~~~~~~ 0', + {file_name='test/unit/buffer_spec.lua'}) + + statusline_test_align('should work with 10 %=', 50, + 'aaaa%=b%=c%=d%=e%=fg%=hi%=jk%=lmnop%=qrstuv%=wxyz', + 'aaaa~~b~~c~~d~~e~~fg~~hi~~jk~~lmnop~~qrstuv~~~wxyz') + + -- stl item testing + local tabline = '' + for i = 1, 1000 do + tabline = tabline .. (i % 2 == 0 and '%#TabLineSel#' or '%#TabLineFill#') .. tostring(i % 2) + end + statusline_test('should handle a large amount of any items', 20, + tabline, + '<1010101010101010101') -- Should not show any error + statusline_test('should handle a larger amount of = than stl initial item', 20, + ('%='):rep(STL_INITIAL_ITEMS * 5), + ' ') -- Should not show any error + statusline_test('should handle many extra characters', 20, + 'a' .. ('a'):rep(STL_INITIAL_ITEMS * 5), + '<aaaaaaaaaaaaaaaaaaa') -- Does not show any error + statusline_test('should handle many extra characters and flags', 20, + 'a' .. ('%=a'):rep(STL_INITIAL_ITEMS * 2), + 'a<aaaaaaaaaaaaaaaaaa') -- Should not show any error + + -- multi-byte testing + statusline_test('should handle multibyte characters', 10, + 'Ĉ%=x', 'Ĉ x') + statusline_test('should handle multibyte characters and different fillchars', 10, + 'Ą%=mid%=end', 'Ą@mid@@end', + {fillchar=('@'):byte()}) + + -- escaping % testing + statusline_test('should handle escape of %', 4, 'abc%%', 'abc%') + statusline_test('case where escaped % does not fit', 3, 'abc%%abcabc', '<bc') + statusline_test('escaped % is first', 1, '%%', '%') + +end) diff --git a/test/unit/strings_spec.lua b/test/unit/strings_spec.lua index b2c839f25c..6d7aceb4b2 100644 --- a/test/unit/strings_spec.lua +++ b/test/unit/strings_spec.lua @@ -139,6 +139,99 @@ describe('vim_strchr()', function() end) end) +describe('vim_snprintf()', function() + local function a(expected, buf, bsize, fmt, ...) + eq(#expected, strings.vim_snprintf(buf, bsize, fmt, ...)) + if bsize > 0 then + local actual = ffi.string(buf, math.min(#expected + 1, bsize)) + eq(expected:sub(1, bsize - 1) .. '\0', actual) + end + end + + local function i(n) return ffi.cast('int', n) end + local function l(n) return ffi.cast('long', n) end + local function ll(n) return ffi.cast('long long', n) end + local function z(n) return ffi.cast('ptrdiff_t', n) end + local function u(n) return ffi.cast('unsigned', n) end + local function ul(n) return ffi.cast('unsigned long', n) end + local function ull(n) return ffi.cast('unsigned long long', n) end + local function uz(n) return ffi.cast('size_t', n) end + + itp('truncation', function() + for bsize = 0, 14 do + local buf = ffi.gc(strings.xmalloc(bsize), strings.xfree) + a('1.00000001e7', buf, bsize, '%.8g', 10000000.1) + a('1234567', buf, bsize, '%d', i(1234567)) + a('1234567', buf, bsize, '%ld', l(1234567)) + a(' 1234567', buf, bsize, '%9ld', l(1234567)) + a('1234567 ', buf, bsize, '%-9ld', l(1234567)) + a('deadbeef', buf, bsize, '%x', u(0xdeadbeef)) + a('001100', buf, bsize, '%06b', u(12)) + a('one two', buf, bsize, '%s %s', 'one', 'two') + a('1.234000', buf, bsize, '%f', 1.234) + a('1.234000e+00', buf, bsize, '%e', 1.234) + a('nan', buf, bsize, '%f', 0.0 / 0.0) + a('inf', buf, bsize, '%f', 1.0 / 0.0) + a('-inf', buf, bsize, '%f', -1.0 / 0.0) + a('-0.000000', buf, bsize, '%f', -0.0) + a('漢語', buf, bsize, '%s', '漢語') + a(' 漢語', buf, bsize, '%8s', '漢語') + a('漢語 ', buf, bsize, '%-8s', '漢語') + a('漢', buf, bsize, '%.3s', '漢語') + a(' foo', buf, bsize, '%5S', 'foo') + a('%%%', buf, bsize, '%%%%%%') + a('0x87654321', buf, bsize, '%p', ffi.cast('char *', 0x87654321)) + a('0x0087654321', buf, bsize, '%012p', ffi.cast('char *', 0x87654321)) + end + end) + + itp('positional arguments', function() + for bsize = 0, 24 do + local buf = ffi.gc(strings.xmalloc(bsize), strings.xfree) + a('1234567 ', buf, bsize, '%1$*2$ld', l(1234567), i(-9)) + a('1234567 ', buf, bsize, '%1$*2$.*3$ld', l(1234567), i(-9), i(5)) + a('1234567 ', buf, bsize, '%1$*3$.*2$ld', l(1234567), i(5), i(-9)) + a('1234567 ', buf, bsize, '%3$*1$.*2$ld', i(-9), i(5), l(1234567)) + a('1234567', buf, bsize, '%1$ld', l(1234567)) + a(' 1234567', buf, bsize, '%1$*2$ld', l(1234567), i(9)) + a('9 12345 7654321', buf, bsize, '%2$ld %1$d %3$lu', i(12345), l(9), ul(7654321)) + a('9 1234567 7654321', buf, bsize, '%2$d %1$ld %3$lu', l(1234567), i(9), ul(7654321)) + a('9 1234567 7654321', buf, bsize, '%2$d %1$lld %3$lu', ll(1234567), i(9), ul(7654321)) + a('9 12345 7654321', buf, bsize, '%2$ld %1$u %3$lu', u(12345), l(9), ul(7654321)) + a('9 1234567 7654321', buf, bsize, '%2$d %1$lu %3$lu', ul(1234567), i(9), ul(7654321)) + a('9 1234567 7654321', buf, bsize, '%2$d %1$llu %3$lu', ull(1234567), i(9), ul(7654321)) + a('9 deadbeef 7654321', buf, bsize, '%2$d %1$x %3$lu', u(0xdeadbeef), i(9), ul(7654321)) + a('9 c 7654321', buf, bsize, '%2$ld %1$c %3$lu', i(('c'):byte()), l(9), ul(7654321)) + a('9 hi 7654321', buf, bsize, '%2$ld %1$s %3$lu', 'hi', l(9), ul(7654321)) + a('9 0.000000e+00 7654321', buf, bsize, '%2$ld %1$e %3$lu', 0.0, l(9), ul(7654321)) + a('two one two', buf, bsize, '%2$s %1$s %2$s', 'one', 'two', 'three') + a('three one two', buf, bsize, '%3$s %1$s %2$s', 'one', 'two', 'three') + a('1234567', buf, bsize, '%1$d', i(1234567)) + a('deadbeef', buf, bsize, '%1$x', u(0xdeadbeef)) + a('001100', buf, bsize, '%2$0*1$b', i(6), u(12)) + a('001100', buf, bsize, '%1$0.*2$b', u(12), i(6)) + a('one two', buf, bsize, '%1$s %2$s', 'one', 'two') + a('001100', buf, bsize, '%06b', u(12)) + a('two one', buf, bsize, '%2$s %1$s', 'one', 'two') + a('1.234000', buf, bsize, '%1$f', 1.234) + a('1.234000e+00', buf, bsize, '%1$e', 1.234) + a('nan', buf, bsize, '%1$f', 0.0 / 0.0) + a('inf', buf, bsize, '%1$f', 1.0 / 0.0) + a('-inf', buf, bsize, '%1$f', -1.0 / 0.0) + a('-0.000000', buf, bsize, '%1$f', -0.0) + end + end) + + itp('%zd and %zu', function() + local bsize = 20 + local buf = ffi.gc(strings.xmalloc(bsize), strings.xfree) + a('-1234567 -7654321', buf, bsize, '%zd %zd', z(-1234567), z(-7654321)) + a('-7654321 -1234567', buf, bsize, '%2$zd %1$zd', z(-1234567), z(-7654321)) + a('1234567 7654321', buf, bsize, '%zu %zu', uz(1234567), uz(7654321)) + a('7654321 1234567', buf, bsize, '%2$zu %1$zu', uz(1234567), uz(7654321)) + end) +end) + describe('strcase_save()' , function() local strcase_save = function(input_string, upper) local res = strings.strcase_save(to_cstr(input_string), upper) diff --git a/test/unit/tempfile_spec.lua b/test/unit/tempfile_spec.lua index 44bd19c1d2..e35490a561 100644 --- a/test/unit/tempfile_spec.lua +++ b/test/unit/tempfile_spec.lua @@ -1,4 +1,3 @@ -local lfs = require('lfs') local helpers = require('test.unit.helpers')(after_each) local itp = helpers.gen_itp(it) @@ -30,7 +29,7 @@ describe('tempfile related functions', function() -- os_file_is_writable returns 2 for a directory which we have rights -- to write into. eq(lib.os_file_is_writable(helpers.to_cstr(dir)), 2) - for entry in lfs.dir(dir) do + for entry in vim.fs.dir(dir) do assert.True(entry == '.' or entry == '..') end end) diff --git a/test/unit/tui_spec.lua b/test/unit/tui_spec.lua deleted file mode 100644 index 25b70a17c2..0000000000 --- a/test/unit/tui_spec.lua +++ /dev/null @@ -1,162 +0,0 @@ -local helpers = require("test.unit.helpers")(after_each) -local cimport = helpers.cimport -local eq = helpers.eq -local ffi = helpers.ffi -local itp = helpers.gen_itp(it) -local to_cstr = helpers.to_cstr - -local cinput = cimport("./src/nvim/tui/input.h") -local rbuffer = cimport("./test/unit/fixtures/rbuffer.h") -local globals = cimport("./src/nvim/globals.h") -local multiqueue = cimport("./test/unit/fixtures/multiqueue.h") -local ui_client = cimport("./src/nvim/ui_client.h") - -itp('handle_background_color', function() - local handle_background_color = cinput.ut_handle_background_color - local term_input = ffi.new('TermInput', {}) - local events = globals.main_loop.thread_events - local kIncomplete = cinput.kIncomplete - local kNotApplicable = cinput.kNotApplicable - local kComplete = cinput.kComplete - - -- Short-circuit when not waiting for response. - term_input.waiting_for_bg_response = 0 - eq(kNotApplicable, handle_background_color(term_input)) - - local capacity = 100 - local rbuf = ffi.gc(rbuffer.rbuffer_new(capacity), rbuffer.rbuffer_free) - term_input.read_stream.buffer = rbuf - - local function assert_bg(colorspace, color, bg) - local term_response = '\027]11;'..colorspace..':'..color..'\007' - rbuffer.rbuffer_write(rbuf, to_cstr(term_response), #term_response) - - term_input.waiting_for_bg_response = 1 - eq(kComplete, handle_background_color(term_input)) - eq(0, term_input.waiting_for_bg_response) - eq(0, multiqueue.multiqueue_size(events)) - eq(bg, ({[0]="light", [1] = "dark", [-1] = "none"}) - [tonumber(ui_client.ui_client_bg_response)]) - - -- Buffer has been consumed. - eq(0, rbuf.size) - end - - assert_bg('rgb', '0000/0000/0000', 'dark') - assert_bg('rgb', 'ffff/ffff/ffff', 'light') - assert_bg('rgb', '000/000/000', 'dark') - assert_bg('rgb', 'fff/fff/fff', 'light') - assert_bg('rgb', '00/00/00', 'dark') - assert_bg('rgb', 'ff/ff/ff', 'light') - assert_bg('rgb', '0/0/0', 'dark') - assert_bg('rgb', 'f/f/f', 'light') - - assert_bg('rgb', 'f/0/0', 'dark') - assert_bg('rgb', '0/f/0', 'light') - assert_bg('rgb', '0/0/f', 'dark') - - assert_bg('rgb', '1/1/1', 'dark') - assert_bg('rgb', '2/2/2', 'dark') - assert_bg('rgb', '3/3/3', 'dark') - assert_bg('rgb', '4/4/4', 'dark') - assert_bg('rgb', '5/5/5', 'dark') - assert_bg('rgb', '6/6/6', 'dark') - assert_bg('rgb', '7/7/7', 'dark') - assert_bg('rgb', '8/8/8', 'light') - assert_bg('rgb', '9/9/9', 'light') - assert_bg('rgb', 'a/a/a', 'light') - assert_bg('rgb', 'b/b/b', 'light') - assert_bg('rgb', 'c/c/c', 'light') - assert_bg('rgb', 'd/d/d', 'light') - assert_bg('rgb', 'e/e/e', 'light') - - assert_bg('rgb', '0/e/0', 'light') - assert_bg('rgb', '0/d/0', 'light') - assert_bg('rgb', '0/c/0', 'dark') - assert_bg('rgb', '0/b/0', 'dark') - - assert_bg('rgb', 'f/0/f', 'dark') - assert_bg('rgb', 'f/1/f', 'dark') - assert_bg('rgb', 'f/2/f', 'dark') - assert_bg('rgb', 'f/3/f', 'light') - assert_bg('rgb', 'f/4/f', 'light') - - assert_bg('rgba', '0000/0000/0000/0000', 'dark') - assert_bg('rgba', '0000/0000/0000/ffff', 'dark') - assert_bg('rgba', 'ffff/ffff/ffff/0000', 'light') - assert_bg('rgba', 'ffff/ffff/ffff/ffff', 'light') - assert_bg('rgba', '000/000/000/000', 'dark') - assert_bg('rgba', '000/000/000/fff', 'dark') - assert_bg('rgba', 'fff/fff/fff/000', 'light') - assert_bg('rgba', 'fff/fff/fff/fff', 'light') - assert_bg('rgba', '00/00/00/00', 'dark') - assert_bg('rgba', '00/00/00/ff', 'dark') - assert_bg('rgba', 'ff/ff/ff/00', 'light') - assert_bg('rgba', 'ff/ff/ff/ff', 'light') - assert_bg('rgba', '0/0/0/0', 'dark') - assert_bg('rgba', '0/0/0/f', 'dark') - assert_bg('rgba', 'f/f/f/0', 'light') - assert_bg('rgba', 'f/f/f/f', 'light') - - - -- Incomplete sequence: necessarily correct behavior. - local term_response = '\027]11;rgba:f/f/f/f' -- missing '\007 - rbuffer.rbuffer_write(rbuf, to_cstr(term_response), #term_response) - - term_input.waiting_for_bg_response = 1 - eq(kIncomplete, handle_background_color(term_input)) - eq(1, term_input.waiting_for_bg_response) - eq(#term_response, rbuf.size) - - term_response = '\007' - rbuffer.rbuffer_write(rbuf, to_cstr(term_response), #term_response) - eq(kComplete, handle_background_color(term_input)) - eq(0, term_input.waiting_for_bg_response) - - eq(0, tonumber(ui_client.ui_client_bg_response)) - eq(0, multiqueue.multiqueue_size(events)) - eq(0, rbuf.size) - - term_response = '\027]11;rg' - rbuffer.rbuffer_write(rbuf, to_cstr(term_response), #term_response) - - term_input.waiting_for_bg_response = 1 - eq(kIncomplete, handle_background_color(term_input)) - eq(1, term_input.waiting_for_bg_response) - eq(#term_response, rbuf.size) - - term_response = 'ba:f/f/f/f\007' - rbuffer.rbuffer_write(rbuf, to_cstr(term_response), #term_response) - eq(kComplete, handle_background_color(term_input)) - eq(0, term_input.waiting_for_bg_response) - - eq(0, tonumber(ui_client.ui_client_bg_response)) - eq(0, multiqueue.multiqueue_size(events)) - eq(0, rbuf.size) - - - -- Does nothing when not at start of buffer. - term_response = '123\027]11;rgba:f/f/f/f\007456' - rbuffer.rbuffer_write(rbuf, to_cstr(term_response), #term_response) - - term_input.waiting_for_bg_response = 3 - eq(kNotApplicable, handle_background_color(term_input)) - eq(2, term_input.waiting_for_bg_response) - - eq(0, multiqueue.multiqueue_size(events)) - eq(#term_response, rbuf.size) - rbuffer.rbuffer_consumed(rbuf, #term_response) - - - -- Keeps trailing buffer. - term_response = '\027]11;rgba:f/f/f/f\007456' - rbuffer.rbuffer_write(rbuf, to_cstr(term_response), #term_response) - - term_input.waiting_for_bg_response = 1 - eq(kComplete, handle_background_color(term_input)) - eq(0, term_input.waiting_for_bg_response) - - eq(0, multiqueue.multiqueue_size(events)) - eq(3, rbuf.size) - rbuffer.rbuffer_consumed(rbuf, rbuf.size) -end) diff --git a/test/unit/undo_spec.lua b/test/unit/undo_spec.lua index f7f8d26d58..ee4203b94c 100644 --- a/test/unit/undo_spec.lua +++ b/test/unit/undo_spec.lua @@ -1,6 +1,6 @@ local helpers = require('test.unit.helpers')(after_each) local itp = helpers.gen_itp(it) -local lfs = require('lfs') +local luv = require('luv') local child_call_once = helpers.child_call_once local sleep = helpers.sleep @@ -9,11 +9,9 @@ local cimport = helpers.cimport local to_cstr = helpers.to_cstr local neq = helpers.neq local eq = helpers.eq +local mkdir = helpers.mkdir -cimport('./src/nvim/ex_cmds_defs.h') -cimport('./src/nvim/buffer_defs.h') -local options = cimport('./src/nvim/option_defs.h') --- TODO: remove: local vim = cimport('./src/nvim/vim.h') +local options = cimport('./src/nvim/option_vars.h') local undo = cimport('./src/nvim/undo.h') local buffer = cimport('./src/nvim/buffer.h') @@ -37,21 +35,21 @@ child_call_once(function() -- requires refactor of UNDO_HASH_SIZE into constant/enum for ffi -- -- compute a hash for this undofile - buffer_hash = ffi.new('char_u[32]') + buffer_hash = ffi.new('char[32]') undo.u_compute_hash(file_buffer, buffer_hash) end) describe('u_write_undo', function() setup(function() - lfs.mkdir('unit-test-directory') - lfs.chdir('unit-test-directory') - options.p_udir = to_cstr(lfs.currentdir()) -- set p_udir to be the test dir + mkdir('unit-test-directory') + luv.chdir('unit-test-directory') + options.p_udir = to_cstr(luv.cwd()) -- set p_udir to be the test dir end) teardown(function() - lfs.chdir('..') - local success, err = lfs.rmdir('unit-test-directory') + luv.chdir('..') + local success, err = luv.fs_rmdir('unit-test-directory') if not success then print(err) -- inform tester if directory fails to delete end @@ -102,7 +100,7 @@ describe('u_write_undo', function() local test_permission_file = io.open(test_file_name, "w") test_permission_file:write("testing permissions") test_permission_file:close() - local test_permissions = lfs.attributes(test_file_name).permissions + local test_permissions = luv.fs_stat(test_file_name).mode -- Create vim buffer local c_file = to_cstr(test_file_name) @@ -115,7 +113,7 @@ describe('u_write_undo', function() local undo_file_name = ffi.string(undo.u_get_undo_file_name(file_buffer.b_ffname, false)) -- Find out the permissions of the new file - local permissions = lfs.attributes(undo_file_name).permissions + local permissions = luv.fs_stat(undo_file_name).mode eq(test_permissions, permissions) -- delete the file now that we're done with it. @@ -130,7 +128,7 @@ describe('u_write_undo', function() end) itp('writes an undofile only readable by the user if the buffer is unnamed', function() - local correct_permissions = "rw-------" + local correct_permissions = 33152 local undo_file_name = "test.undo" -- Create vim buffer @@ -140,7 +138,7 @@ describe('u_write_undo', function() u_write_undo(undo_file_name, false, file_buffer, buffer_hash) -- Find out the permissions of the new file - local permissions = lfs.attributes(undo_file_name).permissions + local permissions = luv.fs_stat(undo_file_name).mode eq(correct_permissions, permissions) -- delete the file now that we're done with it. @@ -172,13 +170,13 @@ describe('u_write_undo', function() u_write_undo(nil, false, file_buffer, buffer_hash) local correct_name = ffi.string(undo.u_get_undo_file_name(file_buffer.b_ffname, false)) - local file_last_modified = lfs.attributes(correct_name).modification + local file_last_modified = luv.fs_stat(correct_name).mtime.sec sleep(1000) -- Ensure difference in timestamps. file_buffer.b_u_numhead = 1 -- Mark it as if there are changes u_write_undo(nil, false, file_buffer, buffer_hash) - local file_last_modified_2 = lfs.attributes(correct_name).modification + local file_last_modified_2 = luv.fs_stat(correct_name).mtime.sec -- print(file_last_modified, file_last_modified_2) neq(file_last_modified, file_last_modified_2) |