neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

luaeval_spec.lua (20620B)


      1 -- Test suite for testing luaeval() function
      2 local t = require('test.testutil')
      3 local n = require('test.functional.testnvim')()
      4 local Screen = require('test.functional.ui.screen')
      5 
      6 local pcall_err = t.pcall_err
      7 local exc_exec = n.exc_exec
      8 local remove_trace = t.remove_trace
      9 local exec_lua = n.exec_lua
     10 local command = n.command
     11 local api = n.api
     12 local fn = n.fn
     13 local clear = n.clear
     14 local eval = n.eval
     15 local feed = n.feed
     16 local assert_alive = n.assert_alive
     17 local NIL = vim.NIL
     18 local eq = t.eq
     19 local matches = t.matches
     20 
     21 before_each(clear)
     22 
     23 local function startswith(expected, actual)
     24  eq(expected, actual:sub(1, #expected))
     25 end
     26 
     27 describe('luaeval()', function()
     28  local nested_by_level = {}
     29  local nested = {}
     30  local nested_s = '{}'
     31  for i = 1, 100 do
     32    if i % 2 == 0 then
     33      nested = { nested }
     34      nested_s = '{' .. nested_s .. '}'
     35    else
     36      nested = { nested = nested }
     37      nested_s = '{nested=' .. nested_s .. '}'
     38    end
     39    nested_by_level[i] = { o = nested, s = nested_s }
     40  end
     41 
     42  describe('second argument', function()
     43    it('is successfully received', function()
     44      local q = {
     45        t = true,
     46        f = false, --[[n=NIL,]]
     47        d = { l = { 'string', 42, 0.42 } },
     48      }
     49      eq(q, fn.luaeval('_A', q))
     50      -- Not tested: nil, funcrefs, returned object identity: behaviour will
     51      -- most likely change.
     52    end)
     53  end)
     54  describe('lua values', function()
     55    it('are successfully transformed', function()
     56      eq(
     57        { n = 1, f = 1.5, s = 'string', l = { 4, 2 } },
     58        fn.luaeval('{n=1, f=1.5, s="string", l={4, 2}}')
     59      )
     60      -- Not tested: nil inside containers: behaviour will most likely change.
     61      eq(NIL, fn.luaeval('nil'))
     62      eq({ [''] = 1 }, fn.luaeval('{[""]=1}'))
     63    end)
     64  end)
     65  describe('recursive lua values', function()
     66    it('are successfully transformed', function()
     67      command('lua rawset(_G, "d", {})')
     68      command('lua rawset(d, "d", d)')
     69      eq("\n{'d': {...@0}}", fn.execute('echo luaeval("d")'))
     70 
     71      command('lua rawset(_G, "l", {})')
     72      command('lua table.insert(l, l)')
     73      eq('\n[[...@0]]', fn.execute('echo luaeval("l")'))
     74    end)
     75  end)
     76  describe('strings with NULs', function()
     77    it('are successfully converted to blobs', function()
     78      command([[let s = luaeval('"\0"')]])
     79      eq('\000', api.nvim_get_var('s'))
     80    end)
     81    it('are successfully converted to special dictionaries in table keys', function()
     82      command([[let d = luaeval('{["\0"]=1}')]])
     83      eq({ _TYPE = {}, _VAL = { { '\000', 1 } } }, api.nvim_get_var('d'))
     84      eq(1, fn.eval('d._TYPE is v:msgpack_types.map'))
     85      eq(eval('v:t_blob'), fn.eval('type(d._VAL[0][0])'))
     86    end)
     87    it('are successfully converted to blobs from a list', function()
     88      command([[let l = luaeval('{"abc", "a\0b", "c\0d", "def"}')]])
     89      eq({ 'abc', 'a\000b', 'c\000d', 'def' }, api.nvim_get_var('l'))
     90    end)
     91  end)
     92 
     93  -- Not checked: funcrefs converted to NIL. To be altered to something more
     94  -- meaningful later.
     95 
     96  it('scalars', function()
     97    -- Also test method call (->) syntax
     98    eq(1, fn.luaeval('1'))
     99    eq(0, eval('"1"->luaeval()->type()'))
    100 
    101    eq(1.5, fn.luaeval('1.5'))
    102    eq(5, eval('"1.5"->luaeval()->type()'))
    103 
    104    eq('test', fn.luaeval('"test"'))
    105    eq(1, eval('"\'test\'"->luaeval()->type()'))
    106 
    107    eq('', fn.luaeval('""'))
    108    eq('\000', fn.luaeval([['\0']]))
    109    eq('\000\n\000', fn.luaeval([['\0\n\0']]))
    110    eq(10, eval([[type(luaeval("'\\0\\n\\0'"))]]))
    111 
    112    eq(true, fn.luaeval('true'))
    113    eq(false, fn.luaeval('false'))
    114    eq(NIL, fn.luaeval('nil'))
    115  end)
    116 
    117  it('containers', function()
    118    eq({}, fn.luaeval('{}'))
    119    eq(3, eval('type(luaeval("{}"))'))
    120 
    121    eq({ test = 1, foo = 2 }, fn.luaeval('{test=1, foo=2}'))
    122    eq(4, eval('type(luaeval("{test=1, foo=2}"))'))
    123 
    124    eq({ 4, 2 }, fn.luaeval('{4, 2}'))
    125    eq(3, eval('type(luaeval("{4, 2}"))'))
    126 
    127    eq({ NIL, 20 }, fn.luaeval('{[2] = 20}'))
    128    eq(3, eval('type(luaeval("{[2] = 20}"))'))
    129 
    130    eq({ 10, NIL, 30 }, fn.luaeval('{[1] = 10, [3] = 30}'))
    131    eq(3, eval('type(luaeval("{[1] = 10, [3] = 30}"))'))
    132 
    133    local level = 30
    134    eq(nested_by_level[level].o, fn.luaeval(nested_by_level[level].s))
    135 
    136    eq(
    137      { _TYPE = {}, _VAL = { { '\000\n\000', '\000\n\000\000' } } },
    138      fn.luaeval([[{['\0\n\0']='\0\n\0\0'}]])
    139    )
    140    eq(1, eval([[luaeval('{["\0\n\0"]="\0\n\0\0"}')._TYPE is v:msgpack_types.map]]))
    141    eq(eval('v:t_blob'), eval([[type(luaeval('{["\0\n\0"]="\0\n\0\0"}')._VAL[0][0])]]))
    142    eq(
    143      { nested = { { _TYPE = {}, _VAL = { { '\000\n\000', '\000\n\000\000' } } } } },
    144      fn.luaeval([[{nested={{['\0\n\0']='\0\n\0\0'}}}]])
    145    )
    146  end)
    147 
    148  it('passes scalars as argument', function()
    149    eq(1, fn.luaeval('_A', 1))
    150    eq(1.5, fn.luaeval('_A', 1.5))
    151    eq('', fn.luaeval('_A', ''))
    152    eq('test', fn.luaeval('_A', 'test'))
    153    eq(NIL, fn.luaeval('_A', NIL))
    154    eq(true, fn.luaeval('_A', true))
    155    eq(false, fn.luaeval('_A', false))
    156  end)
    157 
    158  it('passes containers as argument', function()
    159    eq({}, fn.luaeval('_A', {}))
    160    eq({ test = 1 }, fn.luaeval('_A', { test = 1 }))
    161    eq({ 4, 2 }, fn.luaeval('_A', { 4, 2 }))
    162    local level = 28
    163    eq(nested_by_level[level].o, fn.luaeval('_A', nested_by_level[level].o))
    164  end)
    165 
    166  local function sp(typ, val)
    167    return ('{"_TYPE": v:msgpack_types.%s, "_VAL": %s}'):format(typ, val)
    168  end
    169  local function mapsp(...)
    170    local val = ''
    171    for i = 1, (select('#', ...) / 2) do
    172      val = ('%s[%s,%s],'):format(val, select(i * 2 - 1, ...), select(i * 2, ...))
    173    end
    174    return sp('map', '[' .. val .. ']')
    175  end
    176  local function luaevalarg(argexpr, expr)
    177    return eval((([=[
    178      [
    179        extend(g:, {'_ret': luaeval(%s, %s)})._ret,
    180        type(g:_ret)==type({})&&has_key(g:_ret, '_TYPE')
    181        ? [
    182          get(keys(filter(copy(v:msgpack_types), 'v:val is g:_ret._TYPE')), 0,
    183              g:_ret._TYPE),
    184          get(g:_ret, '_VAL', g:_ret)
    185        ]
    186        : [0, g:_ret]][1]
    187    ]=]):format(expr or '"_A"', argexpr):gsub('\n', '')))
    188  end
    189 
    190  it('passes special dictionaries', function()
    191    eq({ 0, '\000\n\000' }, luaevalarg(sp('string', '["\\n", "\\n"]')))
    192    eq({ 0, true }, luaevalarg(sp('boolean', 1)))
    193    eq({ 0, false }, luaevalarg(sp('boolean', 0)))
    194    eq({ 0, NIL }, luaevalarg(sp('nil', 0)))
    195    eq({ 0, { [''] = '' } }, luaevalarg(mapsp(sp('string', '[""]'), '""')))
    196  end)
    197 
    198  it('failure modes', function()
    199    eq(
    200      'Vim(call):E5100: Cannot convert given Lua table: table should contain either only integer keys or only string keys',
    201      exc_exec('call luaeval("{1, foo=2}")')
    202    )
    203 
    204    startswith('Vim(call):E5107: Lua: [string "luaeval()"]:', exc_exec('call luaeval("1, 2, 3")'))
    205    startswith('Vim(call):E5108: Lua: [string "luaeval()"]:', exc_exec('call luaeval("(nil)()")'))
    206  end)
    207 
    208  it('handles sending lua functions to viml', function()
    209    eq(
    210      true,
    211      exec_lua [[
    212      can_pass_lua_callback_to_vim_from_lua_result = nil
    213 
    214      vim.fn.call(function()
    215        can_pass_lua_callback_to_vim_from_lua_result = true
    216      end, {})
    217 
    218      return can_pass_lua_callback_to_vim_from_lua_result
    219    ]]
    220    )
    221  end)
    222 
    223  it('run functions even in timers', function()
    224    eq(
    225      true,
    226      exec_lua [[
    227      can_pass_lua_callback_to_vim_from_lua_result = nil
    228 
    229      vim.fn.timer_start(50, function()
    230        can_pass_lua_callback_to_vim_from_lua_result = true
    231      end)
    232 
    233      vim.wait(1000, function()
    234        return can_pass_lua_callback_to_vim_from_lua_result
    235      end)
    236 
    237      return can_pass_lua_callback_to_vim_from_lua_result
    238    ]]
    239    )
    240  end)
    241 
    242  it('can run named functions more than once', function()
    243    eq(
    244      5,
    245      exec_lua [[
    246      count_of_vals = 0
    247 
    248      vim.fn.timer_start(5, function()
    249        count_of_vals = count_of_vals + 1
    250      end, {['repeat'] = 5})
    251 
    252      vim.fn.wait(1000, function()
    253        return count_of_vals >= 5
    254      end)
    255 
    256      return count_of_vals
    257    ]]
    258    )
    259  end)
    260 
    261  it('can handle clashing names', function()
    262    eq(
    263      1,
    264      exec_lua [[
    265      local f_loc = function() return 1 end
    266 
    267      local result = nil
    268      vim.fn.timer_start(100, function()
    269        result = f_loc()
    270      end)
    271 
    272      local f_loc = function() return 2 end
    273      vim.wait(1000, function() return result ~= nil end)
    274 
    275      return result
    276    ]]
    277    )
    278  end)
    279 
    280  it('can handle functions with errors', function()
    281    eq(
    282      true,
    283      exec_lua [[
    284      vim.fn.timer_start(10, function()
    285        error("dead function")
    286      end)
    287 
    288      vim.wait(1000, function() return false end)
    289 
    290      return true
    291    ]]
    292    )
    293    -- v:errmsg is set properly #35554
    294    matches(': dead function\n', api.nvim_get_vvar('errmsg'))
    295  end)
    296 
    297  it('can pass functions around', function()
    298    command [[
    299      function VimCanCallLuaCallbacks(Concat, Cb)
    300        let message = a:Concat("Hello Vim", "I'm Lua")
    301        call a:Cb(message)
    302      endfunction
    303    ]]
    304 
    305    eq(
    306      "Hello Vim I'm Lua",
    307      exec_lua [[
    308      can_pass_lua_callback_to_vim_from_lua_result = ""
    309 
    310      vim.fn.VimCanCallLuaCallbacks(
    311        function(greeting, message) return greeting .. " " .. message end,
    312        function(message) can_pass_lua_callback_to_vim_from_lua_result = message end
    313      )
    314 
    315      return can_pass_lua_callback_to_vim_from_lua_result
    316    ]]
    317    )
    318  end)
    319 
    320  it('handles funcrefs', function()
    321    command [[
    322      function VimCanCallLuaCallbacks(Concat, Cb)
    323        let message = a:Concat("Hello Vim", "I'm Lua")
    324        call a:Cb(message)
    325      endfunction
    326    ]]
    327 
    328    eq(
    329      "Hello Vim I'm Lua",
    330      exec_lua [[
    331      can_pass_lua_callback_to_vim_from_lua_result = ""
    332 
    333      vim.funcref('VimCanCallLuaCallbacks')(
    334        function(greeting, message) return greeting .. " " .. message end,
    335        function(message) can_pass_lua_callback_to_vim_from_lua_result = message end
    336      )
    337 
    338      return can_pass_lua_callback_to_vim_from_lua_result
    339    ]]
    340    )
    341  end)
    342 
    343  it('works with metatables using __call', function()
    344    eq(
    345      1,
    346      exec_lua [[
    347      local this_is_local_variable = false
    348      local callable_table = setmetatable({x = 1}, {
    349        __call = function(t, ...)
    350          this_is_local_variable = t.x
    351        end
    352      })
    353 
    354      vim.fn.timer_start(5, callable_table)
    355 
    356      vim.wait(1000, function()
    357        return this_is_local_variable
    358      end)
    359 
    360      return this_is_local_variable
    361    ]]
    362    )
    363  end)
    364 
    365  it('handles being called from a timer once.', function()
    366    eq(
    367      3,
    368      exec_lua [[
    369      local this_is_local_variable = false
    370      local callable_table = setmetatable({5, 4, 3, 2, 1}, {
    371        __call = function(t, ...) this_is_local_variable = t[3] end
    372      })
    373 
    374      vim.fn.timer_start(5, callable_table)
    375      vim.wait(1000, function()
    376        return this_is_local_variable
    377      end)
    378 
    379      return this_is_local_variable
    380    ]]
    381    )
    382  end)
    383 
    384  it('calls functions once with __call metamethod', function()
    385    eq(
    386      true,
    387      exec_lua [[
    388      local this_is_local_variable = false
    389      local callable_table = setmetatable({a = true, b = false}, {
    390        __call = function(t, ...) this_is_local_variable = t.a end
    391      })
    392 
    393      assert(getmetatable(callable_table).__call)
    394      vim.fn.call(callable_table, {})
    395 
    396      return this_is_local_variable
    397    ]]
    398    )
    399  end)
    400 
    401  it('works with lists using __call', function()
    402    eq(
    403      3,
    404      exec_lua [[
    405      local this_is_local_variable = false
    406      local mt = {
    407        __call = function(t, ...)
    408          this_is_local_variable = t[3]
    409        end
    410      }
    411      local callable_table = setmetatable({5, 4, 3, 2, 1}, mt)
    412 
    413      -- Call it once...
    414      vim.fn.timer_start(5, callable_table)
    415      vim.wait(1000, function()
    416        return this_is_local_variable
    417      end)
    418 
    419      assert(this_is_local_variable)
    420      this_is_local_variable = false
    421 
    422      vim.fn.timer_start(5, callable_table)
    423      vim.wait(1000, function()
    424        return this_is_local_variable
    425      end)
    426 
    427      return this_is_local_variable
    428    ]]
    429    )
    430  end)
    431 
    432  it('fails with tables not using __call', function()
    433    eq(
    434      { false, 'Vim:E921: Invalid callback argument' },
    435      exec_lua [[
    436      local this_is_local_variable = false
    437      local callable_table = setmetatable({x = 1}, {})
    438 
    439      return {pcall(function() vim.fn.timer_start(5, callable_table) end)}
    440    ]]
    441    )
    442  end)
    443 
    444  it('converts containers with type_idx', function()
    445    eq(5, eval('type(luaeval("{[vim.type_idx]=vim.types.float, [vim.val_idx]=0}"))'))
    446    eq(4, eval([[type(luaeval('{[vim.type_idx]=vim.types.dictionary}'))]]))
    447    eq(3, eval([[type(luaeval('{[vim.type_idx]=vim.types.array}'))]]))
    448 
    449    eq({}, fn.luaeval('{[vim.type_idx]=vim.types.array}'))
    450 
    451    -- Presence of type_idx makes Vim ignore some keys
    452    eq(
    453      { 42 },
    454      fn.luaeval('{[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}')
    455    )
    456    eq(
    457      { foo = 2 },
    458      fn.luaeval('{[vim.type_idx]=vim.types.dictionary, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}')
    459    )
    460    eq(10, fn.luaeval('{[vim.type_idx]=vim.types.float, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}'))
    461 
    462    -- The following should not crash
    463    eq({}, fn.luaeval('{[vim.type_idx]=vim.types.dictionary}'))
    464  end)
    465 
    466  it('converts self-containing containers', function()
    467    api.nvim_set_var('l', {})
    468    eval('add(l, l)')
    469    eq(true, eval('luaeval("_A == _A[1]", l)'))
    470    eq(true, eval('luaeval("_A[1] == _A[1][1]", [l])'))
    471    eq(true, eval('luaeval("_A.d == _A.d[1]", {"d": l})'))
    472    eq(true, eval('luaeval("_A ~= _A[1]", [l])'))
    473 
    474    api.nvim_set_var('d', { foo = 42 })
    475    eval('extend(d, {"d": d})')
    476    eq(true, eval('luaeval("_A == _A.d", d)'))
    477    eq(true, eval('luaeval("_A[1] == _A[1].d", [d])'))
    478    eq(true, eval('luaeval("_A.d == _A.d.d", {"d": d})'))
    479    eq(true, eval('luaeval("_A ~= _A.d", {"d": d})'))
    480  end)
    481 
    482  it('fails when doing incorrect things in lua', function()
    483    -- Conversion errors
    484    eq(
    485      'Vim(call):E5108: Lua: [string "luaeval()"]:1: attempt to call field \'xxx_nonexistent_key_xxx\' (a nil value)',
    486      remove_trace(exc_exec([[call luaeval("vim.xxx_nonexistent_key_xxx()")]]))
    487    )
    488    eq(
    489      'Vim(call):E5108: Lua: [string "luaeval()"]:1: ERROR',
    490      remove_trace(exc_exec([[call luaeval("error('ERROR')")]]))
    491    )
    492    eq('Vim(call):E5108: Lua: [NULL]', remove_trace(exc_exec([[call luaeval("error(nil)")]])))
    493  end)
    494 
    495  it('does not leak memory when called with too long line', function()
    496    local s = ('x'):rep(65536)
    497    eq(
    498      'Vim(call):E5107: Lua: [string "luaeval()"]:1: unexpected symbol near \')\'',
    499      exc_exec([[call luaeval("(']] .. s .. [[' + )")]])
    500    )
    501    eq(s, fn.luaeval('"' .. s .. '"'))
    502  end)
    503 end)
    504 
    505 describe('v:lua', function()
    506  before_each(function()
    507    exec_lua([[
    508      function _G.foo(a,b,n)
    509        _G.val = n
    510        return a+b
    511      end
    512      mymod = {}
    513      function mymod.noisy(name)
    514        vim.api.nvim_set_current_line("hey "..name)
    515      end
    516      function mymod.crashy()
    517        nonexistent()
    518      end
    519      function mymod.whatis(value)
    520        return type(value) .. ": " .. tostring(value)
    521      end
    522      function mymod.omni(findstart, base)
    523        if findstart == 1 then
    524          return 5
    525        else
    526          if base == 'st' then
    527            return {'stuff', 'steam', 'strange things'}
    528          end
    529        end
    530      end
    531    ]])
    532  end)
    533 
    534  it('in expressions', function()
    535    eq(7, eval('v:lua.foo(3,4,v:null)'))
    536    eq(true, exec_lua([[return _G.val == vim.NIL]]))
    537    eq(NIL, eval('v:lua.mymod.noisy("eval")'))
    538    eq('hey eval', api.nvim_get_current_line())
    539    eq('string: abc', eval('v:lua.mymod.whatis(0z616263)'))
    540    eq('string: ', eval('v:lua.mymod.whatis(v:_null_blob)'))
    541 
    542    eq(
    543      'Vim:E5108: Lua: [string "<nvim>"]:0: attempt to call global \'nonexistent\' (a nil value)',
    544      pcall_err(eval, 'v:lua.mymod.crashy()')
    545    )
    546  end)
    547 
    548  it('when called as a method', function()
    549    eq(123, eval('110->v:lua.foo(13)'))
    550    eq(true, exec_lua([[return _G.val == nil]]))
    551 
    552    eq(321, eval('300->v:lua.foo(21, "boop")'))
    553    eq('boop', exec_lua([[return _G.val]]))
    554 
    555    eq(NIL, eval('"there"->v:lua.mymod.noisy()'))
    556    eq('hey there', api.nvim_get_current_line())
    557    eq({ 5, 10, 15, 20 }, eval('[[1], [2, 3], [4]]->v:lua.vim.tbl_flatten()->map({_, v -> v * 5})'))
    558 
    559    eq(
    560      'Vim:E5108: Lua: [string "<nvim>"]:0: attempt to call global \'nonexistent\' (a nil value)',
    561      pcall_err(eval, '"huh?"->v:lua.mymod.crashy()')
    562    )
    563  end)
    564 
    565  it('in :call', function()
    566    command(":call v:lua.mymod.noisy('command')")
    567    eq('hey command', api.nvim_get_current_line())
    568    eq(
    569      'Vim(call):E5108: Lua: [string "<nvim>"]:0: attempt to call global \'nonexistent\' (a nil value)',
    570      pcall_err(command, 'call v:lua.mymod.crashy()')
    571    )
    572  end)
    573 
    574  it('in func options', function()
    575    local screen = Screen.new(60, 8)
    576    api.nvim_set_option_value('omnifunc', 'v:lua.mymod.omni', {})
    577    feed('isome st<c-x><c-o>')
    578    screen:expect {
    579      grid = [[
    580      some stuff^                                                  |
    581      {1:~   }{12: stuff          }{1:                                        }|
    582      {1:~   }{4: steam          }{1:                                        }|
    583      {1:~   }{4: strange things }{1:                                        }|
    584      {1:~                                                           }|*3
    585      {5:-- Omni completion (^O^N^P) }{6:match 1 of 3}                    |
    586    ]],
    587    }
    588    api.nvim_set_option_value('operatorfunc', 'v:lua.mymod.noisy', {})
    589    feed('<Esc>g@g@')
    590    eq('hey line', api.nvim_get_current_line())
    591  end)
    592 
    593  it('supports packages', function()
    594    command('set pp+=test/functional/fixtures')
    595    eq('\tbadval', eval("v:lua.require'leftpad'('badval')"))
    596    eq(9003, eval("v:lua.require'bar'.doit()"))
    597    eq(9004, eval("v:lua.require'baz-quux'.doit()"))
    598    eq(9003, eval("1 ? v:lua.require'bar'.doit() : v:lua.require'baz-quux'.doit()"))
    599    eq(9004, eval("0 ? v:lua.require'bar'.doit() : v:lua.require'baz-quux'.doit()"))
    600  end)
    601 
    602  it('fails for invalid usage', function()
    603    eq(
    604      [[Vim(let):E15: Invalid expression: "v:lua.func"]],
    605      pcall_err(command, 'let g:Func = v:lua.func')
    606    )
    607    eq([[Vim(let):E15: Invalid expression: "v:lua"]], pcall_err(command, 'let g:Func = v:lua'))
    608    eq(
    609      [[Vim(let):E15: Invalid expression: "v:['lua']"]],
    610      pcall_err(command, "let g:Func = v:['lua']")
    611    )
    612 
    613    eq([[Vim:E15: Invalid expression: "v:['lua'].foo()"]], pcall_err(eval, "v:['lua'].foo()"))
    614    eq(
    615      "Vim(call):E461: Illegal variable name: v:['lua']",
    616      pcall_err(command, "call v:['lua'].baar()")
    617    )
    618    eq('Vim:E1085: Not a callable type: v:lua', pcall_err(eval, 'v:lua()'))
    619 
    620    eq(
    621      'Vim(let):E46: Cannot change read-only variable "v:[\'lua\']"',
    622      pcall_err(command, "let v:['lua'] = 'xx'")
    623    )
    624    eq(
    625      'Vim(let):E46: Cannot change read-only variable "v:lua"',
    626      pcall_err(command, "let v:lua = 'xx'")
    627    )
    628 
    629    eq('Vim:E107: Missing parentheses: v:lua.func', pcall_err(eval, "'bad'->v:lua.func"))
    630    eq(
    631      'Vim:E274: No white space allowed before parenthesis',
    632      pcall_err(eval, "'bad'->v:lua.func ()")
    633    )
    634    eq('Vim:E107: Missing parentheses: v:lua', pcall_err(eval, "'bad'->v:lua"))
    635    eq('Vim:E1085: Not a callable type: v:lua', pcall_err(eval, "'bad'->v:lua()"))
    636    eq([[Vim:E15: Invalid expression: "v:lua.()"]], pcall_err(eval, "'bad'->v:lua.()"))
    637 
    638    eq('Vim:E1085: Not a callable type: v:lua', pcall_err(eval, 'v:lua()'))
    639    eq([[Vim:E15: Invalid expression: "v:lua.()"]], pcall_err(eval, 'v:lua.()'))
    640  end)
    641 
    642  describe('invalid usage in fold text', function()
    643    before_each(function()
    644      feed('ifoo<CR>bar<Esc>')
    645      command('1,2fold')
    646    end)
    647 
    648    it('with missing function name when used as simple function', function()
    649      api.nvim_set_option_value('debug', 'throw', {})
    650      eq(
    651        [[Vim(eval):E15: Invalid expression: "v:lua.()"]],
    652        pcall_err(command, 'set foldtext=v:lua.() | eval foldtextresult(1)')
    653      )
    654    end)
    655 
    656    it('with missing function name when used in expression', function()
    657      api.nvim_set_option_value('debug', 'throw', {})
    658      eq(
    659        [[Vim(eval):E15: Invalid expression: "+v:lua.()"]],
    660        pcall_err(command, 'set foldtext=+v:lua.() | eval foldtextresult(1)')
    661      )
    662    end)
    663 
    664    it('with non-existent function when used as simple function', function()
    665      command('set foldtext=v:lua.NoSuchFunc() | eval foldtextresult(1)')
    666      assert_alive()
    667    end)
    668 
    669    it('with non-existent function when used in expression', function()
    670      command('set foldtext=+v:lua.NoSuchFunc() | eval foldtextresult(1)')
    671      assert_alive()
    672    end)
    673  end)
    674 end)