From 678a51b1da0c0535299341e7a598c080adcf8553 Mon Sep 17 00:00:00 2001 From: Hirokazu Hata Date: Mon, 28 Oct 2019 20:52:18 +0900 Subject: Lua: vim.validate() We often want to do type checking of public function arguments. - test: Rename utility_function_spec.lua to vim_spec.lua - .luacov: Map lua module names --- test/functional/lua/utility_functions_spec.lua | 405 ---------------------- test/functional/lua/vim_spec.lua | 458 +++++++++++++++++++++++++ 2 files changed, 458 insertions(+), 405 deletions(-) delete mode 100644 test/functional/lua/utility_functions_spec.lua create mode 100644 test/functional/lua/vim_spec.lua (limited to 'test/functional/lua') diff --git a/test/functional/lua/utility_functions_spec.lua b/test/functional/lua/utility_functions_spec.lua deleted file mode 100644 index 7dc97ee5af..0000000000 --- a/test/functional/lua/utility_functions_spec.lua +++ /dev/null @@ -1,405 +0,0 @@ --- Test suite for testing interactions with API bindings -local helpers = require('test.functional.helpers')(after_each) -local Screen = require('test.functional.ui.screen') - -local funcs = helpers.funcs -local meths = helpers.meths -local command = helpers.command -local clear = helpers.clear -local eq = helpers.eq -local eval = helpers.eval -local feed = helpers.feed -local pcall_err = helpers.pcall_err -local exec_lua = helpers.exec_lua -local matches = helpers.matches -local source = helpers.source -local NIL = helpers.NIL -local retry = helpers.retry - -before_each(clear) - -describe('lua stdlib', function() - -- İ: `tolower("İ")` is `i` which has length 1 while `İ` itself has - -- length 2 (in bytes). - -- Ⱥ: `tolower("Ⱥ")` is `ⱥ` which has length 2 while `Ⱥ` itself has - -- length 3 (in bytes). - -- - -- Note: 'i' !=? 'İ' and 'ⱥ' !=? 'Ⱥ' on some systems. - -- Note: Built-in Nvim comparison (on systems lacking `strcasecmp`) works - -- only on ASCII characters. - it('vim.stricmp', function() - eq(0, funcs.luaeval('vim.stricmp("a", "A")')) - eq(0, funcs.luaeval('vim.stricmp("A", "a")')) - eq(0, funcs.luaeval('vim.stricmp("a", "a")')) - eq(0, funcs.luaeval('vim.stricmp("A", "A")')) - - eq(0, funcs.luaeval('vim.stricmp("", "")')) - eq(0, funcs.luaeval('vim.stricmp("\\0", "\\0")')) - eq(0, funcs.luaeval('vim.stricmp("\\0\\0", "\\0\\0")')) - eq(0, funcs.luaeval('vim.stricmp("\\0\\0\\0", "\\0\\0\\0")')) - eq(0, funcs.luaeval('vim.stricmp("\\0\\0\\0A", "\\0\\0\\0a")')) - eq(0, funcs.luaeval('vim.stricmp("\\0\\0\\0a", "\\0\\0\\0A")')) - eq(0, funcs.luaeval('vim.stricmp("\\0\\0\\0a", "\\0\\0\\0a")')) - - eq(0, funcs.luaeval('vim.stricmp("a\\0", "A\\0")')) - eq(0, funcs.luaeval('vim.stricmp("A\\0", "a\\0")')) - eq(0, funcs.luaeval('vim.stricmp("a\\0", "a\\0")')) - eq(0, funcs.luaeval('vim.stricmp("A\\0", "A\\0")')) - - eq(0, funcs.luaeval('vim.stricmp("\\0a", "\\0A")')) - eq(0, funcs.luaeval('vim.stricmp("\\0A", "\\0a")')) - eq(0, funcs.luaeval('vim.stricmp("\\0a", "\\0a")')) - eq(0, funcs.luaeval('vim.stricmp("\\0A", "\\0A")')) - - eq(0, funcs.luaeval('vim.stricmp("\\0a\\0", "\\0A\\0")')) - eq(0, funcs.luaeval('vim.stricmp("\\0A\\0", "\\0a\\0")')) - eq(0, funcs.luaeval('vim.stricmp("\\0a\\0", "\\0a\\0")')) - eq(0, funcs.luaeval('vim.stricmp("\\0A\\0", "\\0A\\0")')) - - eq(-1, funcs.luaeval('vim.stricmp("a", "B")')) - eq(-1, funcs.luaeval('vim.stricmp("A", "b")')) - eq(-1, funcs.luaeval('vim.stricmp("a", "b")')) - eq(-1, funcs.luaeval('vim.stricmp("A", "B")')) - - eq(-1, funcs.luaeval('vim.stricmp("", "\\0")')) - eq(-1, funcs.luaeval('vim.stricmp("\\0", "\\0\\0")')) - eq(-1, funcs.luaeval('vim.stricmp("\\0\\0", "\\0\\0\\0")')) - eq(-1, funcs.luaeval('vim.stricmp("\\0\\0\\0A", "\\0\\0\\0b")')) - eq(-1, funcs.luaeval('vim.stricmp("\\0\\0\\0a", "\\0\\0\\0B")')) - eq(-1, funcs.luaeval('vim.stricmp("\\0\\0\\0a", "\\0\\0\\0b")')) - - eq(-1, funcs.luaeval('vim.stricmp("a\\0", "B\\0")')) - eq(-1, funcs.luaeval('vim.stricmp("A\\0", "b\\0")')) - eq(-1, funcs.luaeval('vim.stricmp("a\\0", "b\\0")')) - eq(-1, funcs.luaeval('vim.stricmp("A\\0", "B\\0")')) - - eq(-1, funcs.luaeval('vim.stricmp("\\0a", "\\0B")')) - eq(-1, funcs.luaeval('vim.stricmp("\\0A", "\\0b")')) - eq(-1, funcs.luaeval('vim.stricmp("\\0a", "\\0b")')) - eq(-1, funcs.luaeval('vim.stricmp("\\0A", "\\0B")')) - - eq(-1, funcs.luaeval('vim.stricmp("\\0a\\0", "\\0B\\0")')) - eq(-1, funcs.luaeval('vim.stricmp("\\0A\\0", "\\0b\\0")')) - eq(-1, funcs.luaeval('vim.stricmp("\\0a\\0", "\\0b\\0")')) - eq(-1, funcs.luaeval('vim.stricmp("\\0A\\0", "\\0B\\0")')) - - eq(1, funcs.luaeval('vim.stricmp("c", "B")')) - eq(1, funcs.luaeval('vim.stricmp("C", "b")')) - eq(1, funcs.luaeval('vim.stricmp("c", "b")')) - eq(1, funcs.luaeval('vim.stricmp("C", "B")')) - - eq(1, funcs.luaeval('vim.stricmp("\\0", "")')) - eq(1, funcs.luaeval('vim.stricmp("\\0\\0", "\\0")')) - eq(1, funcs.luaeval('vim.stricmp("\\0\\0\\0", "\\0\\0")')) - eq(1, funcs.luaeval('vim.stricmp("\\0\\0\\0\\0", "\\0\\0\\0")')) - eq(1, funcs.luaeval('vim.stricmp("\\0\\0\\0C", "\\0\\0\\0b")')) - eq(1, funcs.luaeval('vim.stricmp("\\0\\0\\0c", "\\0\\0\\0B")')) - eq(1, funcs.luaeval('vim.stricmp("\\0\\0\\0c", "\\0\\0\\0b")')) - - eq(1, funcs.luaeval('vim.stricmp("c\\0", "B\\0")')) - eq(1, funcs.luaeval('vim.stricmp("C\\0", "b\\0")')) - eq(1, funcs.luaeval('vim.stricmp("c\\0", "b\\0")')) - eq(1, funcs.luaeval('vim.stricmp("C\\0", "B\\0")')) - - eq(1, funcs.luaeval('vim.stricmp("c\\0", "B")')) - eq(1, funcs.luaeval('vim.stricmp("C\\0", "b")')) - eq(1, funcs.luaeval('vim.stricmp("c\\0", "b")')) - eq(1, funcs.luaeval('vim.stricmp("C\\0", "B")')) - - eq(1, funcs.luaeval('vim.stricmp("\\0c", "\\0B")')) - eq(1, funcs.luaeval('vim.stricmp("\\0C", "\\0b")')) - eq(1, funcs.luaeval('vim.stricmp("\\0c", "\\0b")')) - eq(1, funcs.luaeval('vim.stricmp("\\0C", "\\0B")')) - - eq(1, funcs.luaeval('vim.stricmp("\\0c\\0", "\\0B\\0")')) - eq(1, funcs.luaeval('vim.stricmp("\\0C\\0", "\\0b\\0")')) - eq(1, funcs.luaeval('vim.stricmp("\\0c\\0", "\\0b\\0")')) - eq(1, funcs.luaeval('vim.stricmp("\\0C\\0", "\\0B\\0")')) - end) - - it("vim.str_utfindex/str_byteindex", function() - exec_lua([[_G.test_text = "xy åäö ɧ 汉语 ↥ 🤦x🦄 å بِيَّ"]]) - local indicies32 = {[0]=0,1,2,3,5,7,9,10,12,13,16,19,20,23,24,28,29,33,34,35,37,38,40,42,44,46,48} - local indicies16 = {[0]=0,1,2,3,5,7,9,10,12,13,16,19,20,23,24,28,28,29,33,33,34,35,37,38,40,42,44,46,48} - for i,k in pairs(indicies32) do - eq(k, exec_lua("return vim.str_byteindex(_G.test_text, ...)", i), i) - end - for i,k in pairs(indicies16) do - eq(k, exec_lua("return vim.str_byteindex(_G.test_text, ..., true)", i), i) - end - local i32, i16 = 0, 0 - for k = 0,48 do - if indicies32[i32] < k then - i32 = i32 + 1 - end - if indicies16[i16] < k then - i16 = i16 + 1 - if indicies16[i16+1] == indicies16[i16] then - i16 = i16 + 1 - end - end - eq({i32, i16}, exec_lua("return {vim.str_utfindex(_G.test_text, ...)}", k), k) - end - end) - - it("vim.schedule", function() - exec_lua([[ - test_table = {} - vim.schedule(function() - table.insert(test_table, "xx") - end) - table.insert(test_table, "yy") - ]]) - eq({"yy","xx"}, exec_lua("return test_table")) - - -- Validates args. - eq('Error executing lua: vim.schedule: expected function', - pcall_err(exec_lua, "vim.schedule('stringly')")) - eq('Error executing lua: vim.schedule: expected function', - pcall_err(exec_lua, "vim.schedule()")) - - exec_lua([[ - vim.schedule(function() - error("big failure\nvery async") - end) - ]]) - - feed("") - eq('Error executing vim.schedule lua callback: [string ""]:2: big failure\nvery async', eval("v:errmsg")) - - local screen = Screen.new(60,5) - screen:set_default_attr_ids({ - [1] = {bold = true, foreground = Screen.colors.Blue1}, - [2] = {bold = true, reverse = true}, - [3] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red}, - [4] = {bold = true, foreground = Screen.colors.SeaGreen4}, - }) - screen:attach() - screen:expect{grid=[[ - ^ | - {1:~ }| - {1:~ }| - {1:~ }| - | - ]]} - - -- nvim_command causes a vimL exception, check that it is properly caught - -- and propagated as an error message in async contexts.. #10809 - exec_lua([[ - vim.schedule(function() - vim.api.nvim_command(":echo 'err") - end) - ]]) - screen:expect{grid=[[ - | - {2: }| - {3:Error executing vim.schedule lua callback: [string ""]}| - {3::2: Vim(echo):E115: Missing quote: 'err} | - {4:Press ENTER or type command to continue}^ | - ]]} - end) - - it("vim.split", function() - local split = function(str, sep, plain) - return exec_lua('return vim.split(...)', str, sep, plain) - end - - local tests = { - { "a,b", ",", false, { 'a', 'b' } }, - { ":aa::bb:", ":", false, { '', 'aa', '', 'bb', '' } }, - { "::ee::ff:", ":", false, { '', '', 'ee', '', 'ff', '' } }, - { "ab", ".", false, { '', '', '' } }, - { "a1b2c", "[0-9]", false, { 'a', 'b', 'c' } }, - { "xy", "", false, { 'x', 'y' } }, - { "here be dragons", " ", false, { "here", "be", "dragons"} }, - { "axaby", "ab?", false, { '', 'x', 'y' } }, - { "f v2v v3v w2w ", "([vw])2%1", false, { 'f ', ' v3v ', ' ' } }, - { "x*yz*oo*l", "*", true, { 'x', 'yz', 'oo', 'l' } }, - } - - for _, t in ipairs(tests) do - eq(t[4], split(t[1], t[2], t[3])) - end - - local loops = { - { "abc", ".-" }, - } - - for _, t in ipairs(loops) do - matches(".*Infinite loop detected", pcall_err(split, t[1], t[2])) - end - - -- Validates args. - eq(true, pcall(split, 'string', 'string', nil)) - eq('Error executing lua: .../shared.lua: Expected string, got number', - pcall_err(split, 1, 'string', nil)) - eq('Error executing lua: .../shared.lua: Expected string, got number', - pcall_err(split, 'string', 1, nil)) - eq('Error executing lua: .../shared.lua: Expected boolean or nil, got number', - pcall_err(split, 'string', 'string', 1)) - end) - - it('vim.trim', function() - local trim = function(s) - return exec_lua('return vim.trim(...)', s) - end - - local trims = { - { " a", "a" }, - { " b ", "b" }, - { "\tc" , "c" }, - { "r\n", "r" }, - } - - for _, t in ipairs(trims) do - assert(t[2], trim(t[1])) - end - - -- Validates args. - eq('Error executing lua: .../shared.lua: Expected string, got number', - pcall_err(trim, 2)) - end) - - it('vim.inspect', function() - -- just make sure it basically works, it has its own test suite - local inspect = function(t, opts) - return exec_lua('return vim.inspect(...)', t, opts) - end - - eq('2', inspect(2)) - eq('{+a = {+b = 1+}+}', - inspect({ a = { b = 1 } }, { newline = '+', indent = '' })) - - -- special value vim.inspect.KEY works - eq('{ KEY_a = "x", KEY_b = "y"}', exec_lua([[ - return vim.inspect({a="x", b="y"}, {newline = '', process = function(item, path) - if path[#path] == vim.inspect.KEY then - return 'KEY_'..item - end - return item - end}) - ]])) - end) - - it("vim.deepcopy", function() - local is_dc = exec_lua([[ - local a = { x = { 1, 2 }, y = 5} - local b = vim.deepcopy(a) - - local count = 0 - for _ in pairs(b) do count = count + 1 end - - return b.x[1] == 1 and b.x[2] == 2 and b.y == 5 and count == 2 - and tostring(a) ~= tostring(b) - ]]) - - assert(is_dc) - end) - - it('vim.pesc', function() - eq('foo%-bar', exec_lua([[return vim.pesc('foo-bar')]])) - eq('foo%%%-bar', exec_lua([[return vim.pesc(vim.pesc('foo-bar'))]])) - - -- Validates args. - eq("Error executing lua: .../shared.lua: Expected string, got number", - pcall_err(exec_lua, [[return vim.pesc(2)]])) - end) - - it('vim.call, vim.fn', function() - eq(true, exec_lua([[return vim.call('sin', 0.0) == 0.0 ]])) - eq(true, exec_lua([[return vim.fn.sin(0.0) == 0.0 ]])) - -- compat: nvim_call_function uses "special" value for vimL float - eq(false, exec_lua([[return vim.api.nvim_call_function('sin', {0.0}) == 0.0 ]])) - - source([[ - func! FooFunc(test) - let g:test = a:test - return {} - endfunc - func! VarArg(...) - return a:000 - endfunc - func! Nilly() - return [v:null, v:null] - endfunc - ]]) - eq(true, exec_lua([[return next(vim.fn.FooFunc(3)) == nil ]])) - eq(3, eval("g:test")) - -- compat: nvim_call_function uses "special" value for empty dict - eq(true, exec_lua([[return next(vim.api.nvim_call_function("FooFunc", {5})) == true ]])) - eq(5, eval("g:test")) - - eq({2, "foo", true}, exec_lua([[return vim.fn.VarArg(2, "foo", true)]])) - - eq(true, exec_lua([[ - local x = vim.fn.Nilly() - return #x == 2 and x[1] == vim.NIL and x[2] == vim.NIL - ]])) - eq({NIL, NIL}, exec_lua([[return vim.fn.Nilly()]])) - - -- error handling - eq({false, 'Vim:E714: List required'}, exec_lua([[return {pcall(vim.fn.add, "aa", "bb")}]])) - end) - - it('vim.rpcrequest and vim.rpcnotify', function() - exec_lua([[ - chan = vim.fn.jobstart({'cat'}, {rpc=true}) - vim.rpcrequest(chan, 'nvim_set_current_line', 'meow') - ]]) - eq('meow', meths.get_current_line()) - command("let x = [3, 'aa', v:true, v:null]") - eq(true, exec_lua([[ - ret = vim.rpcrequest(chan, 'nvim_get_var', 'x') - return #ret == 4 and ret[1] == 3 and ret[2] == 'aa' and ret[3] == true and ret[4] == vim.NIL - ]])) - eq({3, 'aa', true, NIL}, exec_lua([[return ret]])) - - -- error handling - eq({false, 'Invalid channel: 23'}, - exec_lua([[return {pcall(vim.rpcrequest, 23, 'foo')}]])) - eq({false, 'Invalid channel: 23'}, - exec_lua([[return {pcall(vim.rpcnotify, 23, 'foo')}]])) - - eq({false, 'Vim:E121: Undefined variable: foobar'}, - exec_lua([[return {pcall(vim.rpcrequest, chan, 'nvim_eval', "foobar")}]])) - - - -- rpcnotify doesn't wait on request - eq('meow', exec_lua([[ - vim.rpcnotify(chan, 'nvim_set_current_line', 'foo') - return vim.api.nvim_get_current_line() - ]])) - retry(10, nil, function() - eq('foo', meths.get_current_line()) - end) - - local screen = Screen.new(50,7) - screen:set_default_attr_ids({ - [1] = {bold = true, foreground = Screen.colors.Blue1}, - [2] = {bold = true, reverse = true}, - [3] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red}, - [4] = {bold = true, foreground = Screen.colors.SeaGreen4}, - }) - screen:attach() - exec_lua([[ - local timer = vim.loop.new_timer() - timer:start(20, 0, function () - -- notify ok (executed later when safe) - vim.rpcnotify(chan, 'nvim_set_var', 'yy', {3, vim.NIL}) - -- rpcrequest an error - vim.rpcrequest(chan, 'nvim_set_current_line', 'bork') - end) - ]]) - screen:expect{grid=[[ - foo | - {1:~ }| - {2: }| - {3:Error executing luv callback:} | - {3:[string ""]:6: E5560: rpcrequest must not be}| - {3: called in a lua loop callback} | - {4:Press ENTER or type command to continue}^ | - ]]} - feed('') - eq({3, NIL}, meths.get_var('yy')) - end) -end) diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua new file mode 100644 index 0000000000..cb1f027623 --- /dev/null +++ b/test/functional/lua/vim_spec.lua @@ -0,0 +1,458 @@ +-- Test suite for testing interactions with API bindings +local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') + +local funcs = helpers.funcs +local meths = helpers.meths +local command = helpers.command +local clear = helpers.clear +local eq = helpers.eq +local eval = helpers.eval +local feed = helpers.feed +local pcall_err = helpers.pcall_err +local exec_lua = helpers.exec_lua +local matches = helpers.matches +local source = helpers.source +local NIL = helpers.NIL +local retry = helpers.retry + +before_each(clear) + +describe('lua stdlib', function() + -- İ: `tolower("İ")` is `i` which has length 1 while `İ` itself has + -- length 2 (in bytes). + -- Ⱥ: `tolower("Ⱥ")` is `ⱥ` which has length 2 while `Ⱥ` itself has + -- length 3 (in bytes). + -- + -- Note: 'i' !=? 'İ' and 'ⱥ' !=? 'Ⱥ' on some systems. + -- Note: Built-in Nvim comparison (on systems lacking `strcasecmp`) works + -- only on ASCII characters. + it('vim.stricmp', function() + eq(0, funcs.luaeval('vim.stricmp("a", "A")')) + eq(0, funcs.luaeval('vim.stricmp("A", "a")')) + eq(0, funcs.luaeval('vim.stricmp("a", "a")')) + eq(0, funcs.luaeval('vim.stricmp("A", "A")')) + + eq(0, funcs.luaeval('vim.stricmp("", "")')) + eq(0, funcs.luaeval('vim.stricmp("\\0", "\\0")')) + eq(0, funcs.luaeval('vim.stricmp("\\0\\0", "\\0\\0")')) + eq(0, funcs.luaeval('vim.stricmp("\\0\\0\\0", "\\0\\0\\0")')) + eq(0, funcs.luaeval('vim.stricmp("\\0\\0\\0A", "\\0\\0\\0a")')) + eq(0, funcs.luaeval('vim.stricmp("\\0\\0\\0a", "\\0\\0\\0A")')) + eq(0, funcs.luaeval('vim.stricmp("\\0\\0\\0a", "\\0\\0\\0a")')) + + eq(0, funcs.luaeval('vim.stricmp("a\\0", "A\\0")')) + eq(0, funcs.luaeval('vim.stricmp("A\\0", "a\\0")')) + eq(0, funcs.luaeval('vim.stricmp("a\\0", "a\\0")')) + eq(0, funcs.luaeval('vim.stricmp("A\\0", "A\\0")')) + + eq(0, funcs.luaeval('vim.stricmp("\\0a", "\\0A")')) + eq(0, funcs.luaeval('vim.stricmp("\\0A", "\\0a")')) + eq(0, funcs.luaeval('vim.stricmp("\\0a", "\\0a")')) + eq(0, funcs.luaeval('vim.stricmp("\\0A", "\\0A")')) + + eq(0, funcs.luaeval('vim.stricmp("\\0a\\0", "\\0A\\0")')) + eq(0, funcs.luaeval('vim.stricmp("\\0A\\0", "\\0a\\0")')) + eq(0, funcs.luaeval('vim.stricmp("\\0a\\0", "\\0a\\0")')) + eq(0, funcs.luaeval('vim.stricmp("\\0A\\0", "\\0A\\0")')) + + eq(-1, funcs.luaeval('vim.stricmp("a", "B")')) + eq(-1, funcs.luaeval('vim.stricmp("A", "b")')) + eq(-1, funcs.luaeval('vim.stricmp("a", "b")')) + eq(-1, funcs.luaeval('vim.stricmp("A", "B")')) + + eq(-1, funcs.luaeval('vim.stricmp("", "\\0")')) + eq(-1, funcs.luaeval('vim.stricmp("\\0", "\\0\\0")')) + eq(-1, funcs.luaeval('vim.stricmp("\\0\\0", "\\0\\0\\0")')) + eq(-1, funcs.luaeval('vim.stricmp("\\0\\0\\0A", "\\0\\0\\0b")')) + eq(-1, funcs.luaeval('vim.stricmp("\\0\\0\\0a", "\\0\\0\\0B")')) + eq(-1, funcs.luaeval('vim.stricmp("\\0\\0\\0a", "\\0\\0\\0b")')) + + eq(-1, funcs.luaeval('vim.stricmp("a\\0", "B\\0")')) + eq(-1, funcs.luaeval('vim.stricmp("A\\0", "b\\0")')) + eq(-1, funcs.luaeval('vim.stricmp("a\\0", "b\\0")')) + eq(-1, funcs.luaeval('vim.stricmp("A\\0", "B\\0")')) + + eq(-1, funcs.luaeval('vim.stricmp("\\0a", "\\0B")')) + eq(-1, funcs.luaeval('vim.stricmp("\\0A", "\\0b")')) + eq(-1, funcs.luaeval('vim.stricmp("\\0a", "\\0b")')) + eq(-1, funcs.luaeval('vim.stricmp("\\0A", "\\0B")')) + + eq(-1, funcs.luaeval('vim.stricmp("\\0a\\0", "\\0B\\0")')) + eq(-1, funcs.luaeval('vim.stricmp("\\0A\\0", "\\0b\\0")')) + eq(-1, funcs.luaeval('vim.stricmp("\\0a\\0", "\\0b\\0")')) + eq(-1, funcs.luaeval('vim.stricmp("\\0A\\0", "\\0B\\0")')) + + eq(1, funcs.luaeval('vim.stricmp("c", "B")')) + eq(1, funcs.luaeval('vim.stricmp("C", "b")')) + eq(1, funcs.luaeval('vim.stricmp("c", "b")')) + eq(1, funcs.luaeval('vim.stricmp("C", "B")')) + + eq(1, funcs.luaeval('vim.stricmp("\\0", "")')) + eq(1, funcs.luaeval('vim.stricmp("\\0\\0", "\\0")')) + eq(1, funcs.luaeval('vim.stricmp("\\0\\0\\0", "\\0\\0")')) + eq(1, funcs.luaeval('vim.stricmp("\\0\\0\\0\\0", "\\0\\0\\0")')) + eq(1, funcs.luaeval('vim.stricmp("\\0\\0\\0C", "\\0\\0\\0b")')) + eq(1, funcs.luaeval('vim.stricmp("\\0\\0\\0c", "\\0\\0\\0B")')) + eq(1, funcs.luaeval('vim.stricmp("\\0\\0\\0c", "\\0\\0\\0b")')) + + eq(1, funcs.luaeval('vim.stricmp("c\\0", "B\\0")')) + eq(1, funcs.luaeval('vim.stricmp("C\\0", "b\\0")')) + eq(1, funcs.luaeval('vim.stricmp("c\\0", "b\\0")')) + eq(1, funcs.luaeval('vim.stricmp("C\\0", "B\\0")')) + + eq(1, funcs.luaeval('vim.stricmp("c\\0", "B")')) + eq(1, funcs.luaeval('vim.stricmp("C\\0", "b")')) + eq(1, funcs.luaeval('vim.stricmp("c\\0", "b")')) + eq(1, funcs.luaeval('vim.stricmp("C\\0", "B")')) + + eq(1, funcs.luaeval('vim.stricmp("\\0c", "\\0B")')) + eq(1, funcs.luaeval('vim.stricmp("\\0C", "\\0b")')) + eq(1, funcs.luaeval('vim.stricmp("\\0c", "\\0b")')) + eq(1, funcs.luaeval('vim.stricmp("\\0C", "\\0B")')) + + eq(1, funcs.luaeval('vim.stricmp("\\0c\\0", "\\0B\\0")')) + eq(1, funcs.luaeval('vim.stricmp("\\0C\\0", "\\0b\\0")')) + eq(1, funcs.luaeval('vim.stricmp("\\0c\\0", "\\0b\\0")')) + eq(1, funcs.luaeval('vim.stricmp("\\0C\\0", "\\0B\\0")')) + end) + + it("vim.str_utfindex/str_byteindex", function() + exec_lua([[_G.test_text = "xy åäö ɧ 汉语 ↥ 🤦x🦄 å بِيَّ"]]) + local indicies32 = {[0]=0,1,2,3,5,7,9,10,12,13,16,19,20,23,24,28,29,33,34,35,37,38,40,42,44,46,48} + local indicies16 = {[0]=0,1,2,3,5,7,9,10,12,13,16,19,20,23,24,28,28,29,33,33,34,35,37,38,40,42,44,46,48} + for i,k in pairs(indicies32) do + eq(k, exec_lua("return vim.str_byteindex(_G.test_text, ...)", i), i) + end + for i,k in pairs(indicies16) do + eq(k, exec_lua("return vim.str_byteindex(_G.test_text, ..., true)", i), i) + end + local i32, i16 = 0, 0 + for k = 0,48 do + if indicies32[i32] < k then + i32 = i32 + 1 + end + if indicies16[i16] < k then + i16 = i16 + 1 + if indicies16[i16+1] == indicies16[i16] then + i16 = i16 + 1 + end + end + eq({i32, i16}, exec_lua("return {vim.str_utfindex(_G.test_text, ...)}", k), k) + end + end) + + it("vim.schedule", function() + exec_lua([[ + test_table = {} + vim.schedule(function() + table.insert(test_table, "xx") + end) + table.insert(test_table, "yy") + ]]) + eq({"yy","xx"}, exec_lua("return test_table")) + + -- Validates args. + eq('Error executing lua: vim.schedule: expected function', + pcall_err(exec_lua, "vim.schedule('stringly')")) + eq('Error executing lua: vim.schedule: expected function', + pcall_err(exec_lua, "vim.schedule()")) + + exec_lua([[ + vim.schedule(function() + error("big failure\nvery async") + end) + ]]) + + feed("") + eq('Error executing vim.schedule lua callback: [string ""]:2: big failure\nvery async', eval("v:errmsg")) + + local screen = Screen.new(60,5) + screen:set_default_attr_ids({ + [1] = {bold = true, foreground = Screen.colors.Blue1}, + [2] = {bold = true, reverse = true}, + [3] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red}, + [4] = {bold = true, foreground = Screen.colors.SeaGreen4}, + }) + screen:attach() + screen:expect{grid=[[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + -- nvim_command causes a vimL exception, check that it is properly caught + -- and propagated as an error message in async contexts.. #10809 + exec_lua([[ + vim.schedule(function() + vim.api.nvim_command(":echo 'err") + end) + ]]) + screen:expect{grid=[[ + | + {2: }| + {3:Error executing vim.schedule lua callback: [string ""]}| + {3::2: Vim(echo):E115: Missing quote: 'err} | + {4:Press ENTER or type command to continue}^ | + ]]} + end) + + it("vim.split", function() + local split = function(str, sep, plain) + return exec_lua('return vim.split(...)', str, sep, plain) + end + + local tests = { + { "a,b", ",", false, { 'a', 'b' } }, + { ":aa::bb:", ":", false, { '', 'aa', '', 'bb', '' } }, + { "::ee::ff:", ":", false, { '', '', 'ee', '', 'ff', '' } }, + { "ab", ".", false, { '', '', '' } }, + { "a1b2c", "[0-9]", false, { 'a', 'b', 'c' } }, + { "xy", "", false, { 'x', 'y' } }, + { "here be dragons", " ", false, { "here", "be", "dragons"} }, + { "axaby", "ab?", false, { '', 'x', 'y' } }, + { "f v2v v3v w2w ", "([vw])2%1", false, { 'f ', ' v3v ', ' ' } }, + { "x*yz*oo*l", "*", true, { 'x', 'yz', 'oo', 'l' } }, + } + + for _, t in ipairs(tests) do + eq(t[4], split(t[1], t[2], t[3])) + end + + local loops = { + { "abc", ".-" }, + } + + for _, t in ipairs(loops) do + matches(".*Infinite loop detected", pcall_err(split, t[1], t[2])) + end + + -- Validates args. + eq(true, pcall(split, 'string', 'string', nil)) + eq('Error executing lua: .../shared.lua: Expected string, got number', + pcall_err(split, 1, 'string', nil)) + eq('Error executing lua: .../shared.lua: Expected string, got number', + pcall_err(split, 'string', 1, nil)) + eq('Error executing lua: .../shared.lua: Expected boolean or nil, got number', + pcall_err(split, 'string', 'string', 1)) + end) + + it('vim.trim', function() + local trim = function(s) + return exec_lua('return vim.trim(...)', s) + end + + local trims = { + { " a", "a" }, + { " b ", "b" }, + { "\tc" , "c" }, + { "r\n", "r" }, + } + + for _, t in ipairs(trims) do + assert(t[2], trim(t[1])) + end + + -- Validates args. + eq('Error executing lua: .../shared.lua: Expected string, got number', + pcall_err(trim, 2)) + end) + + it('vim.inspect', function() + -- just make sure it basically works, it has its own test suite + local inspect = function(t, opts) + return exec_lua('return vim.inspect(...)', t, opts) + end + + eq('2', inspect(2)) + eq('{+a = {+b = 1+}+}', + inspect({ a = { b = 1 } }, { newline = '+', indent = '' })) + + -- special value vim.inspect.KEY works + eq('{ KEY_a = "x", KEY_b = "y"}', exec_lua([[ + return vim.inspect({a="x", b="y"}, {newline = '', process = function(item, path) + if path[#path] == vim.inspect.KEY then + return 'KEY_'..item + end + return item + end}) + ]])) + end) + + it("vim.deepcopy", function() + local is_dc = exec_lua([[ + local a = { x = { 1, 2 }, y = 5} + local b = vim.deepcopy(a) + + local count = 0 + for _ in pairs(b) do count = count + 1 end + + return b.x[1] == 1 and b.x[2] == 2 and b.y == 5 and count == 2 + and tostring(a) ~= tostring(b) + ]]) + + assert(is_dc) + end) + + it('vim.pesc', function() + eq('foo%-bar', exec_lua([[return vim.pesc('foo-bar')]])) + eq('foo%%%-bar', exec_lua([[return vim.pesc(vim.pesc('foo-bar'))]])) + + -- Validates args. + eq("Error executing lua: .../shared.lua: Expected string, got number", + pcall_err(exec_lua, [[return vim.pesc(2)]])) + end) + + it('vim.call, vim.fn', function() + eq(true, exec_lua([[return vim.call('sin', 0.0) == 0.0 ]])) + eq(true, exec_lua([[return vim.fn.sin(0.0) == 0.0 ]])) + -- compat: nvim_call_function uses "special" value for vimL float + eq(false, exec_lua([[return vim.api.nvim_call_function('sin', {0.0}) == 0.0 ]])) + + source([[ + func! FooFunc(test) + let g:test = a:test + return {} + endfunc + func! VarArg(...) + return a:000 + endfunc + func! Nilly() + return [v:null, v:null] + endfunc + ]]) + eq(true, exec_lua([[return next(vim.fn.FooFunc(3)) == nil ]])) + eq(3, eval("g:test")) + -- compat: nvim_call_function uses "special" value for empty dict + eq(true, exec_lua([[return next(vim.api.nvim_call_function("FooFunc", {5})) == true ]])) + eq(5, eval("g:test")) + + eq({2, "foo", true}, exec_lua([[return vim.fn.VarArg(2, "foo", true)]])) + + eq(true, exec_lua([[ + local x = vim.fn.Nilly() + return #x == 2 and x[1] == vim.NIL and x[2] == vim.NIL + ]])) + eq({NIL, NIL}, exec_lua([[return vim.fn.Nilly()]])) + + -- error handling + eq({false, 'Vim:E714: List required'}, exec_lua([[return {pcall(vim.fn.add, "aa", "bb")}]])) + end) + + it('vim.rpcrequest and vim.rpcnotify', function() + exec_lua([[ + chan = vim.fn.jobstart({'cat'}, {rpc=true}) + vim.rpcrequest(chan, 'nvim_set_current_line', 'meow') + ]]) + eq('meow', meths.get_current_line()) + command("let x = [3, 'aa', v:true, v:null]") + eq(true, exec_lua([[ + ret = vim.rpcrequest(chan, 'nvim_get_var', 'x') + return #ret == 4 and ret[1] == 3 and ret[2] == 'aa' and ret[3] == true and ret[4] == vim.NIL + ]])) + eq({3, 'aa', true, NIL}, exec_lua([[return ret]])) + + -- error handling + eq({false, 'Invalid channel: 23'}, + exec_lua([[return {pcall(vim.rpcrequest, 23, 'foo')}]])) + eq({false, 'Invalid channel: 23'}, + exec_lua([[return {pcall(vim.rpcnotify, 23, 'foo')}]])) + + eq({false, 'Vim:E121: Undefined variable: foobar'}, + exec_lua([[return {pcall(vim.rpcrequest, chan, 'nvim_eval', "foobar")}]])) + + + -- rpcnotify doesn't wait on request + eq('meow', exec_lua([[ + vim.rpcnotify(chan, 'nvim_set_current_line', 'foo') + return vim.api.nvim_get_current_line() + ]])) + retry(10, nil, function() + eq('foo', meths.get_current_line()) + end) + + local screen = Screen.new(50,7) + screen:set_default_attr_ids({ + [1] = {bold = true, foreground = Screen.colors.Blue1}, + [2] = {bold = true, reverse = true}, + [3] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red}, + [4] = {bold = true, foreground = Screen.colors.SeaGreen4}, + }) + screen:attach() + exec_lua([[ + local timer = vim.loop.new_timer() + timer:start(20, 0, function () + -- notify ok (executed later when safe) + vim.rpcnotify(chan, 'nvim_set_var', 'yy', {3, vim.NIL}) + -- rpcrequest an error + vim.rpcrequest(chan, 'nvim_set_current_line', 'bork') + end) + ]]) + screen:expect{grid=[[ + foo | + {1:~ }| + {2: }| + {3:Error executing luv callback:} | + {3:[string ""]:6: E5560: rpcrequest must not be}| + {3: called in a lua loop callback} | + {4:Press ENTER or type command to continue}^ | + ]]} + feed('') + eq({3, NIL}, meths.get_var('yy')) + end) + + it('vim.validate', function() + eq(NIL, exec_lua("vim.validate({ arg1={ {}, 'table' }})")) + eq(NIL, exec_lua("vim.validate({ arg1={ {}, 't' }})")) + eq(NIL, exec_lua("vim.validate({ arg1={ nil, 't', true }})")) + eq(NIL, exec_lua("vim.validate({ arg1={ { foo='foo' }, 't' }})")) + eq(NIL, exec_lua("vim.validate({ arg1={ { 'foo' }, 't' }})")) + eq(NIL, exec_lua("vim.validate({ arg1={ 'foo', 'string' }})")) + eq(NIL, exec_lua("vim.validate({ arg1={ 'foo', 's' }})")) + eq(NIL, exec_lua("vim.validate({ arg1={ '', 's' }})")) + eq(NIL, exec_lua("vim.validate({ arg1={ nil, 's', true }})")) + eq(NIL, exec_lua("vim.validate({ arg1={ 1, 'number' }})")) + eq(NIL, exec_lua("vim.validate({ arg1={ 1, 'n' }})")) + eq(NIL, exec_lua("vim.validate({ arg1={ 0, 'n' }})")) + eq(NIL, exec_lua("vim.validate({ arg1={ 0.1, 'n' }})")) + eq(NIL, exec_lua("vim.validate({ arg1={ nil, 'n', true }})")) + eq(NIL, exec_lua("vim.validate({ arg1={ true, 'boolean' }})")) + eq(NIL, exec_lua("vim.validate({ arg1={ true, 'b' }})")) + eq(NIL, exec_lua("vim.validate({ arg1={ false, 'b' }})")) + eq(NIL, exec_lua("vim.validate({ arg1={ nil, 'b', true }})")) + eq(NIL, exec_lua("vim.validate({ arg1={ function()end, 'function' }})")) + eq(NIL, exec_lua("vim.validate({ arg1={ function()end, 'f' }})")) + eq(NIL, exec_lua("vim.validate({ arg1={ nil, 'f', true }})")) + eq(NIL, exec_lua("vim.validate({ arg1={ nil, 'nil' }})")) + eq(NIL, exec_lua("vim.validate({ arg1={ nil, 'nil', true }})")) + eq(NIL, exec_lua("vim.validate({ arg1={ coroutine.create(function()end), 'thread' }})")) + eq(NIL, exec_lua("vim.validate({ arg1={ nil, 'thread', true }})")) + eq(NIL, exec_lua("vim.validate({ arg1={ {}, 't' } }, { arg2={ 'foo', 's' }})")) + eq(NIL, exec_lua("vim.validate({ arg1={ {}, 't' }, arg2={ 'foo', 's' }})")) + eq(NIL, exec_lua("vim.validate({ arg1={ 2, function(a) return (a % 2) == 0 end, 'even number' }})")) + + eq("Error executing lua: .../shared.lua: arg1: expected table, got number", pcall_err(exec_lua, "vim.validate({ arg1={ 1, 't' }})")) + eq("Error executing lua: .../shared.lua: arg2: expected string, got number", pcall_err(exec_lua, "vim.validate({ arg1={ {}, 't' }, arg2={ 1, 's' }})")) + eq("Error executing lua: .../shared.lua: arg2: expected string, got nil", pcall_err(exec_lua, "vim.validate({ arg1={ {}, 't' }, arg2={ nil, 's' }})")) + eq("Error executing lua: .../shared.lua: arg2: expected string, got nil", pcall_err(exec_lua, "vim.validate({ arg1={ {}, 't' }, arg2={ nil, 's' }})")) + eq("Error executing lua: .../shared.lua: arg1: expected even number, got 3", pcall_err(exec_lua, "vim.validate({ arg1={ 3, function(a) return a == 1 end, 'even number' }})")) + end) + + it('vim.is_callable', function() + eq(true, exec_lua("return vim.is_callable(function()end)")) + eq(true, exec_lua([[ + local meta = { __call = function()end } + local function new_callable() + return setmetatable({}, meta) + end + local callable = new_callable() + return vim.is_callable(callable) + ]])) + + eq(false, exec_lua("return vim.is_callable(1)")) + eq(false, exec_lua("return vim.is_callable('foo')")) + eq(false, exec_lua("return vim.is_callable({})")) + end) +end) -- cgit