luaeval_spec.lua 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. -- Test suite for testing luaeval() function
  2. local helpers = require('test.functional.helpers')(after_each)
  3. local redir_exec = helpers.redir_exec
  4. local exc_exec = helpers.exc_exec
  5. local command = helpers.command
  6. local meths = helpers.meths
  7. local funcs = helpers.funcs
  8. local clear = helpers.clear
  9. local eval = helpers.eval
  10. local NIL = helpers.NIL
  11. local eq = helpers.eq
  12. before_each(clear)
  13. local function startswith(expected, actual)
  14. eq(expected, actual:sub(1, #expected))
  15. end
  16. describe('luaeval()', function()
  17. local nested_by_level = {}
  18. local nested = {}
  19. local nested_s = '{}'
  20. for i=1,100 do
  21. if i % 2 == 0 then
  22. nested = {nested}
  23. nested_s = '{' .. nested_s .. '}'
  24. else
  25. nested = {nested=nested}
  26. nested_s = '{nested=' .. nested_s .. '}'
  27. end
  28. nested_by_level[i] = {o=nested, s=nested_s}
  29. end
  30. describe('second argument', function()
  31. it('is successfully received', function()
  32. local t = {t=true, f=false, --[[n=NIL,]] d={l={'string', 42, 0.42}}}
  33. eq(t, funcs.luaeval("_A", t))
  34. -- Not tested: nil, funcrefs, returned object identity: behaviour will
  35. -- most likely change.
  36. end)
  37. end)
  38. describe('lua values', function()
  39. it('are successfully transformed', function()
  40. eq({n=1, f=1.5, s='string', l={4, 2}},
  41. funcs.luaeval('{n=1, f=1.5, s="string", l={4, 2}}'))
  42. -- Not tested: nil inside containers: behaviour will most likely change.
  43. eq(NIL, funcs.luaeval('nil'))
  44. eq({['']=1}, funcs.luaeval('{[""]=1}'))
  45. end)
  46. end)
  47. describe('recursive lua values', function()
  48. it('are successfully transformed', function()
  49. command('lua rawset(_G, "d", {})')
  50. command('lua rawset(d, "d", d)')
  51. eq('\n{\'d\': {...@0}}', funcs.execute('echo luaeval("d")'))
  52. command('lua rawset(_G, "l", {})')
  53. command('lua table.insert(l, l)')
  54. eq('\n[[...@0]]', funcs.execute('echo luaeval("l")'))
  55. end)
  56. end)
  57. describe('strings', function()
  58. it('are successfully converted to special dictionaries', function()
  59. command([[let s = luaeval('"\0"')]])
  60. eq({_TYPE={}, _VAL={'\n'}}, meths.get_var('s'))
  61. eq(1, funcs.eval('s._TYPE is v:msgpack_types.binary'))
  62. end)
  63. it('are successfully converted to special dictionaries in table keys',
  64. function()
  65. command([[let d = luaeval('{["\0"]=1}')]])
  66. eq({_TYPE={}, _VAL={{{_TYPE={}, _VAL={'\n'}}, 1}}}, meths.get_var('d'))
  67. eq(1, funcs.eval('d._TYPE is v:msgpack_types.map'))
  68. eq(1, funcs.eval('d._VAL[0][0]._TYPE is v:msgpack_types.string'))
  69. end)
  70. it('are successfully converted to special dictionaries from a list',
  71. function()
  72. command([[let l = luaeval('{"abc", "a\0b", "c\0d", "def"}')]])
  73. eq({'abc', {_TYPE={}, _VAL={'a\nb'}}, {_TYPE={}, _VAL={'c\nd'}}, 'def'},
  74. meths.get_var('l'))
  75. eq(1, funcs.eval('l[1]._TYPE is v:msgpack_types.binary'))
  76. eq(1, funcs.eval('l[2]._TYPE is v:msgpack_types.binary'))
  77. end)
  78. end)
  79. -- Not checked: funcrefs converted to NIL. To be altered to something more
  80. -- meaningful later.
  81. it('correctly evaluates scalars', function()
  82. eq(1, funcs.luaeval('1'))
  83. eq(0, eval('type(luaeval("1"))'))
  84. eq(1.5, funcs.luaeval('1.5'))
  85. eq(5, eval('type(luaeval("1.5"))'))
  86. eq("test", funcs.luaeval('"test"'))
  87. eq(1, eval('type(luaeval("\'test\'"))'))
  88. eq('', funcs.luaeval('""'))
  89. eq({_TYPE={}, _VAL={'\n'}}, funcs.luaeval([['\0']]))
  90. eq({_TYPE={}, _VAL={'\n', '\n'}}, funcs.luaeval([['\0\n\0']]))
  91. eq(1, eval([[luaeval('"\0\n\0"')._TYPE is v:msgpack_types.binary]]))
  92. eq(true, funcs.luaeval('true'))
  93. eq(false, funcs.luaeval('false'))
  94. eq(NIL, funcs.luaeval('nil'))
  95. end)
  96. it('correctly evaluates containers', function()
  97. eq({}, funcs.luaeval('{}'))
  98. eq(3, eval('type(luaeval("{}"))'))
  99. eq({test=1, foo=2}, funcs.luaeval('{test=1, foo=2}'))
  100. eq(4, eval('type(luaeval("{test=1, foo=2}"))'))
  101. eq({4, 2}, funcs.luaeval('{4, 2}'))
  102. eq(3, eval('type(luaeval("{4, 2}"))'))
  103. local level = 30
  104. eq(nested_by_level[level].o, funcs.luaeval(nested_by_level[level].s))
  105. eq({_TYPE={}, _VAL={{{_TYPE={}, _VAL={'\n', '\n'}}, {_TYPE={}, _VAL={'\n', '\n\n'}}}}},
  106. funcs.luaeval([[{['\0\n\0']='\0\n\0\0'}]]))
  107. eq(1, eval([[luaeval('{["\0\n\0"]="\0\n\0\0"}')._TYPE is v:msgpack_types.map]]))
  108. eq(1, eval([[luaeval('{["\0\n\0"]="\0\n\0\0"}')._VAL[0][0]._TYPE is v:msgpack_types.string]]))
  109. eq(1, eval([[luaeval('{["\0\n\0"]="\0\n\0\0"}')._VAL[0][1]._TYPE is v:msgpack_types.binary]]))
  110. eq({nested={{_TYPE={}, _VAL={{{_TYPE={}, _VAL={'\n', '\n'}}, {_TYPE={}, _VAL={'\n', '\n\n'}}}}}}},
  111. funcs.luaeval([[{nested={{['\0\n\0']='\0\n\0\0'}}}]]))
  112. end)
  113. it('correctly passes scalars as argument', function()
  114. eq(1, funcs.luaeval('_A', 1))
  115. eq(1.5, funcs.luaeval('_A', 1.5))
  116. eq('', funcs.luaeval('_A', ''))
  117. eq('test', funcs.luaeval('_A', 'test'))
  118. eq(NIL, funcs.luaeval('_A', NIL))
  119. eq(true, funcs.luaeval('_A', true))
  120. eq(false, funcs.luaeval('_A', false))
  121. end)
  122. it('correctly passes containers as argument', function()
  123. eq({}, funcs.luaeval('_A', {}))
  124. eq({test=1}, funcs.luaeval('_A', {test=1}))
  125. eq({4, 2}, funcs.luaeval('_A', {4, 2}))
  126. local level = 28
  127. eq(nested_by_level[level].o, funcs.luaeval('_A', nested_by_level[level].o))
  128. end)
  129. local function sp(typ, val)
  130. return ('{"_TYPE": v:msgpack_types.%s, "_VAL": %s}'):format(typ, val)
  131. end
  132. local function mapsp(...)
  133. local val = ''
  134. for i=1,(select('#', ...)/2) do
  135. val = ('%s[%s,%s],'):format(val, select(i * 2 - 1, ...),
  136. select(i * 2, ...))
  137. end
  138. return sp('map', '[' .. val .. ']')
  139. end
  140. local function luaevalarg(argexpr, expr)
  141. return eval(([=[
  142. [
  143. extend(g:, {'_ret': luaeval(%s, %s)})._ret,
  144. type(g:_ret)==type({})&&has_key(g:_ret, '_TYPE')
  145. ? [
  146. get(keys(filter(copy(v:msgpack_types), 'v:val is g:_ret._TYPE')), 0,
  147. g:_ret._TYPE),
  148. get(g:_ret, '_VAL', g:_ret)
  149. ]
  150. : [0, g:_ret]][1]
  151. ]=]):format(expr or '"_A"', argexpr):gsub('\n', ''))
  152. end
  153. it('correctly passes special dictionaries', function()
  154. eq({'binary', {'\n', '\n'}}, luaevalarg(sp('binary', '["\\n", "\\n"]')))
  155. eq({'binary', {'\n', '\n'}}, luaevalarg(sp('string', '["\\n", "\\n"]')))
  156. eq({0, true}, luaevalarg(sp('boolean', 1)))
  157. eq({0, false}, luaevalarg(sp('boolean', 0)))
  158. eq({0, NIL}, luaevalarg(sp('nil', 0)))
  159. eq({0, {[""]=""}}, luaevalarg(mapsp(sp('binary', '[""]'), '""')))
  160. eq({0, {[""]=""}}, luaevalarg(mapsp(sp('string', '[""]'), '""')))
  161. end)
  162. it('issues an error in some cases', function()
  163. eq("Vim(call):E5100: Cannot convert given lua table: table should either have a sequence of positive integer keys or contain only string keys",
  164. exc_exec('call luaeval("{1, foo=2}")'))
  165. eq("Vim(call):E5101: Cannot convert given lua type",
  166. exc_exec('call luaeval("vim.api.nvim_buf_get_lines")'))
  167. startswith("Vim(call):E5107: Error while creating lua chunk for luaeval(): ",
  168. exc_exec('call luaeval("1, 2, 3")'))
  169. startswith("Vim(call):E5108: Error while calling lua chunk for luaeval(): ",
  170. exc_exec('call luaeval("(nil)()")'))
  171. eq("Vim(call):E5101: Cannot convert given lua type",
  172. exc_exec('call luaeval("{42, vim.api}")'))
  173. eq("Vim(call):E5101: Cannot convert given lua type",
  174. exc_exec('call luaeval("{foo=42, baz=vim.api}")'))
  175. -- The following should not crash: conversion error happens inside
  176. eq("Vim(call):E5101: Cannot convert given lua type",
  177. exc_exec('call luaeval("vim.api")'))
  178. -- The following should not show internal error
  179. eq("\nE5101: Cannot convert given lua type\n0",
  180. redir_exec('echo luaeval("vim.api")'))
  181. end)
  182. it('correctly converts containers with type_idx', function()
  183. eq(5, eval('type(luaeval("{[vim.type_idx]=vim.types.float, [vim.val_idx]=0}"))'))
  184. eq(4, eval([[type(luaeval('{[vim.type_idx]=vim.types.dictionary}'))]]))
  185. eq(3, eval([[type(luaeval('{[vim.type_idx]=vim.types.array}'))]]))
  186. eq({}, funcs.luaeval('{[vim.type_idx]=vim.types.array}'))
  187. -- Presence of type_idx makes Vim ignore some keys
  188. eq({42}, funcs.luaeval('{[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}'))
  189. eq({foo=2}, funcs.luaeval('{[vim.type_idx]=vim.types.dictionary, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}'))
  190. eq(10, funcs.luaeval('{[vim.type_idx]=vim.types.float, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}'))
  191. -- The following should not crash
  192. eq({}, funcs.luaeval('{[vim.type_idx]=vim.types.dictionary}'))
  193. end)
  194. it('correctly converts self-containing containers', function()
  195. meths.set_var('l', {})
  196. eval('add(l, l)')
  197. eq(true, eval('luaeval("_A == _A[1]", l)'))
  198. eq(true, eval('luaeval("_A[1] == _A[1][1]", [l])'))
  199. eq(true, eval('luaeval("_A.d == _A.d[1]", {"d": l})'))
  200. eq(true, eval('luaeval("_A ~= _A[1]", [l])'))
  201. meths.set_var('d', {foo=42})
  202. eval('extend(d, {"d": d})')
  203. eq(true, eval('luaeval("_A == _A.d", d)'))
  204. eq(true, eval('luaeval("_A[1] == _A[1].d", [d])'))
  205. eq(true, eval('luaeval("_A.d == _A.d.d", {"d": d})'))
  206. eq(true, eval('luaeval("_A ~= _A.d", {"d": d})'))
  207. end)
  208. it('errors out correctly when doing incorrect things in lua', function()
  209. -- Conversion errors
  210. eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string "<VimL compiled string>"]:1: attempt to call field \'xxx_nonexistent_key_xxx\' (a nil value)',
  211. exc_exec([[call luaeval("vim.xxx_nonexistent_key_xxx()")]]))
  212. eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string "<VimL compiled string>"]:1: ERROR',
  213. exc_exec([[call luaeval("error('ERROR')")]]))
  214. eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [NULL]',
  215. exc_exec([[call luaeval("error(nil)")]]))
  216. end)
  217. it('does not leak memory when called with too long line',
  218. function()
  219. local s = ('x'):rep(65536)
  220. eq('Vim(call):E5107: Error while creating lua chunk for luaeval(): [string "<VimL compiled string>"]:1: unexpected symbol near \')\'',
  221. exc_exec([[call luaeval("(']] .. s ..[[' + )")]]))
  222. eq(s, funcs.luaeval('"' .. s .. '"'))
  223. end)
  224. end)