api_spec.lua 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. -- Test suite for testing interactions with API bindings
  2. local helpers = require('test.functional.helpers')(after_each)
  3. local exc_exec = helpers.exc_exec
  4. local remove_trace = helpers.remove_trace
  5. local funcs = helpers.funcs
  6. local clear = helpers.clear
  7. local eval = helpers.eval
  8. local NIL = helpers.NIL
  9. local eq = helpers.eq
  10. local exec_lua = helpers.exec_lua
  11. before_each(clear)
  12. describe('luaeval(vim.api.…)', function()
  13. describe('with channel_id and buffer handle', function()
  14. describe('nvim_buf_get_lines', function()
  15. it('works', function()
  16. funcs.setline(1, {"abc", "def", "a\nb", "ttt"})
  17. eq({'a\000b'},
  18. funcs.luaeval('vim.api.nvim_buf_get_lines(1, 2, 3, false)'))
  19. end)
  20. end)
  21. describe('nvim_buf_set_lines', function()
  22. it('works', function()
  23. funcs.setline(1, {"abc", "def", "a\nb", "ttt"})
  24. eq(NIL, funcs.luaeval('vim.api.nvim_buf_set_lines(1, 1, 2, false, {"b\\0a"})'))
  25. eq({'abc', 'b\000a', 'a\000b', 'ttt'},
  26. funcs.luaeval('vim.api.nvim_buf_get_lines(1, 0, 4, false)'))
  27. end)
  28. end)
  29. end)
  30. describe('with errors', function()
  31. it('transforms API error from nvim_buf_set_lines into lua error', function()
  32. funcs.setline(1, {"abc", "def", "a\nb", "ttt"})
  33. eq({false, "'replacement string' item contains newlines"},
  34. funcs.luaeval('{pcall(vim.api.nvim_buf_set_lines, 1, 1, 2, false, {"b\\na"})}'))
  35. end)
  36. it('transforms API error from nvim_win_set_cursor into lua error', function()
  37. eq({false, 'Argument "pos" must be a [row, col] array'},
  38. funcs.luaeval('{pcall(vim.api.nvim_win_set_cursor, 0, {1, 2, 3})}'))
  39. -- Used to produce a memory leak due to a bug in nvim_win_set_cursor
  40. eq({false, 'Invalid window id: -1'},
  41. funcs.luaeval('{pcall(vim.api.nvim_win_set_cursor, -1, {1, 2, 3})}'))
  42. end)
  43. it('transforms API error from nvim_win_set_cursor + same array as in first test into lua error',
  44. function()
  45. eq({false, 'Argument "pos" must be a [row, col] array'},
  46. funcs.luaeval('{pcall(vim.api.nvim_win_set_cursor, 0, {"b\\na"})}'))
  47. end)
  48. end)
  49. it('correctly evaluates API code which calls luaeval', function()
  50. local str = (([===[vim.api.nvim_eval([==[
  51. luaeval('vim.api.nvim_eval([=[
  52. luaeval("vim.api.nvim_eval([[
  53. luaeval(1)
  54. ]])")
  55. ]=])')
  56. ]==])]===]):gsub('\n', ' '))
  57. eq(1, funcs.luaeval(str))
  58. end)
  59. it('correctly converts from API objects', function()
  60. eq(1, funcs.luaeval('vim.api.nvim_eval("1")'))
  61. eq('1', funcs.luaeval([[vim.api.nvim_eval('"1"')]]))
  62. eq('Blobby', funcs.luaeval('vim.api.nvim_eval("0z426c6f626279")'))
  63. eq({}, funcs.luaeval('vim.api.nvim_eval("[]")'))
  64. eq({}, funcs.luaeval('vim.api.nvim_eval("{}")'))
  65. eq(1, funcs.luaeval('vim.api.nvim_eval("1.0")'))
  66. eq('\000', funcs.luaeval('vim.api.nvim_eval("0z00")'))
  67. eq(true, funcs.luaeval('vim.api.nvim_eval("v:true")'))
  68. eq(false, funcs.luaeval('vim.api.nvim_eval("v:false")'))
  69. eq(NIL, funcs.luaeval('vim.api.nvim_eval("v:null")'))
  70. eq(0, eval([[type(luaeval('vim.api.nvim_eval("1")'))]]))
  71. eq(1, eval([[type(luaeval('vim.api.nvim_eval("''1''")'))]]))
  72. eq(1, eval([[type(luaeval('vim.api.nvim_eval("0zbeef")'))]]))
  73. eq(3, eval([[type(luaeval('vim.api.nvim_eval("[]")'))]]))
  74. eq(4, eval([[type(luaeval('vim.api.nvim_eval("{}")'))]]))
  75. eq(5, eval([[type(luaeval('vim.api.nvim_eval("1.0")'))]]))
  76. eq(6, eval([[type(luaeval('vim.api.nvim_eval("v:true")'))]]))
  77. eq(6, eval([[type(luaeval('vim.api.nvim_eval("v:false")'))]]))
  78. eq(7, eval([[type(luaeval('vim.api.nvim_eval("v:null")'))]]))
  79. eq({foo=42}, funcs.luaeval([[vim.api.nvim_eval('{"foo": 42}')]]))
  80. eq({42}, funcs.luaeval([[vim.api.nvim_eval('[42]')]]))
  81. eq({foo={bar=42}, baz=50}, funcs.luaeval([[vim.api.nvim_eval('{"foo": {"bar": 42}, "baz": 50}')]]))
  82. eq({{42}, {}}, funcs.luaeval([=[vim.api.nvim_eval('[[42], []]')]=]))
  83. end)
  84. it('correctly converts to API objects', function()
  85. eq(1, funcs.luaeval('vim.api.nvim__id(1)'))
  86. eq('1', funcs.luaeval('vim.api.nvim__id("1")'))
  87. eq({1}, funcs.luaeval('vim.api.nvim__id({1})'))
  88. eq({foo=1}, funcs.luaeval('vim.api.nvim__id({foo=1})'))
  89. eq(1.5, funcs.luaeval('vim.api.nvim__id(1.5)'))
  90. eq(true, funcs.luaeval('vim.api.nvim__id(true)'))
  91. eq(false, funcs.luaeval('vim.api.nvim__id(false)'))
  92. eq(NIL, funcs.luaeval('vim.api.nvim__id(nil)'))
  93. -- API strings from Blobs can work as NUL-terminated C strings
  94. eq('Vim(call):E5555: API call: Vim:E15: Invalid expression: ',
  95. exc_exec('call nvim_eval(v:_null_blob)'))
  96. eq('Vim(call):E5555: API call: Vim:E15: Invalid expression: ',
  97. exc_exec('call nvim_eval(0z)'))
  98. eq(1, eval('nvim_eval(0z31)'))
  99. eq(0, eval([[type(luaeval('vim.api.nvim__id(1)'))]]))
  100. eq(1, eval([[type(luaeval('vim.api.nvim__id("1")'))]]))
  101. eq(3, eval([[type(luaeval('vim.api.nvim__id({1})'))]]))
  102. eq(4, eval([[type(luaeval('vim.api.nvim__id({foo=1})'))]]))
  103. eq(5, eval([[type(luaeval('vim.api.nvim__id(1.5)'))]]))
  104. eq(6, eval([[type(luaeval('vim.api.nvim__id(true)'))]]))
  105. eq(6, eval([[type(luaeval('vim.api.nvim__id(false)'))]]))
  106. eq(7, eval([[type(luaeval('vim.api.nvim__id(nil)'))]]))
  107. eq({foo=1, bar={42, {{baz=true}, 5}}}, funcs.luaeval('vim.api.nvim__id({foo=1, bar={42, {{baz=true}, 5}}})'))
  108. eq(true, funcs.luaeval('vim.api.nvim__id(vim.api.nvim__id)(true)'))
  109. eq(42, exec_lua [[
  110. local f = vim.api.nvim__id({42, vim.api.nvim__id})
  111. return f[2](f[1])
  112. ]])
  113. end)
  114. it('correctly converts container objects with type_idx to API objects', function()
  115. eq(5, eval('type(luaeval("vim.api.nvim__id({[vim.type_idx]=vim.types.float, [vim.val_idx]=0})"))'))
  116. eq(4, eval([[type(luaeval('vim.api.nvim__id({[vim.type_idx]=vim.types.dictionary})'))]]))
  117. eq(3, eval([[type(luaeval('vim.api.nvim__id({[vim.type_idx]=vim.types.array})'))]]))
  118. eq({}, funcs.luaeval('vim.api.nvim__id({[vim.type_idx]=vim.types.array})'))
  119. -- Presence of type_idx makes Vim ignore some keys
  120. eq({42}, funcs.luaeval('vim.api.nvim__id({[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2, [1]=42})'))
  121. eq({foo=2}, funcs.luaeval('vim.api.nvim__id({[vim.type_idx]=vim.types.dictionary, [vim.val_idx]=10, [5]=1, foo=2, [1]=42})'))
  122. eq(10, funcs.luaeval('vim.api.nvim__id({[vim.type_idx]=vim.types.float, [vim.val_idx]=10, [5]=1, foo=2, [1]=42})'))
  123. eq({}, funcs.luaeval('vim.api.nvim__id({[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2})'))
  124. end)
  125. it('correctly converts arrays with type_idx to API objects', function()
  126. eq(3, eval([[type(luaeval('vim.api.nvim__id_array({[vim.type_idx]=vim.types.array})'))]]))
  127. eq({}, funcs.luaeval('vim.api.nvim__id_array({[vim.type_idx]=vim.types.array})'))
  128. eq({42}, funcs.luaeval('vim.api.nvim__id_array({[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2, [1]=42})'))
  129. eq({{foo=2}}, funcs.luaeval('vim.api.nvim__id_array({{[vim.type_idx]=vim.types.dictionary, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}})'))
  130. eq({10}, funcs.luaeval('vim.api.nvim__id_array({{[vim.type_idx]=vim.types.float, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}})'))
  131. eq({}, funcs.luaeval('vim.api.nvim__id_array({[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2})'))
  132. eq({}, funcs.luaeval('vim.api.nvim__id_array({})'))
  133. eq(3, eval([[type(luaeval('vim.api.nvim__id_array({})'))]]))
  134. end)
  135. it('correctly converts dictionaries with type_idx to API objects', function()
  136. eq(4, eval([[type(luaeval('vim.api.nvim__id_dictionary({[vim.type_idx]=vim.types.dictionary})'))]]))
  137. eq({}, funcs.luaeval('vim.api.nvim__id_dictionary({[vim.type_idx]=vim.types.dictionary})'))
  138. eq({v={42}}, funcs.luaeval('vim.api.nvim__id_dictionary({v={[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}})'))
  139. eq({foo=2}, funcs.luaeval('vim.api.nvim__id_dictionary({[vim.type_idx]=vim.types.dictionary, [vim.val_idx]=10, [5]=1, foo=2, [1]=42})'))
  140. eq({v=10}, funcs.luaeval('vim.api.nvim__id_dictionary({v={[vim.type_idx]=vim.types.float, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}})'))
  141. eq({v={}}, funcs.luaeval('vim.api.nvim__id_dictionary({v={[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2}})'))
  142. -- If API requests dictionary, then empty table will be the one. This is not
  143. -- the case normally because empty table is an empty array.
  144. eq({}, funcs.luaeval('vim.api.nvim__id_dictionary({})'))
  145. eq(4, eval([[type(luaeval('vim.api.nvim__id_dictionary({})'))]]))
  146. end)
  147. it('errors out correctly when working with API', function()
  148. -- Conversion errors
  149. eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Cannot convert given lua table',
  150. remove_trace(exc_exec([[call luaeval("vim.api.nvim__id({1, foo=42})")]])))
  151. -- Errors in number of arguments
  152. eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected 1 argument',
  153. remove_trace(exc_exec([[call luaeval("vim.api.nvim__id()")]])))
  154. eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected 1 argument',
  155. remove_trace(exc_exec([[call luaeval("vim.api.nvim__id(1, 2)")]])))
  156. eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected 2 arguments',
  157. remove_trace(exc_exec([[call luaeval("vim.api.nvim_set_var(1, 2, 3)")]])))
  158. -- Error in argument types
  159. eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected lua string',
  160. remove_trace(exc_exec([[call luaeval("vim.api.nvim_set_var(1, 2)")]])))
  161. eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected lua number',
  162. remove_trace(exc_exec([[call luaeval("vim.api.nvim_buf_get_lines(0, 'test', 1, false)")]])))
  163. eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Number is not integral',
  164. remove_trace(exc_exec([[call luaeval("vim.api.nvim_buf_get_lines(0, 1.5, 1, false)")]])))
  165. eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected Lua number',
  166. remove_trace(exc_exec([[call luaeval("vim.api.nvim_win_is_valid(nil)")]])))
  167. eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected lua table',
  168. remove_trace(exc_exec([[call luaeval("vim.api.nvim__id_float('test')")]])))
  169. eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Unexpected type',
  170. remove_trace(exc_exec([[call luaeval("vim.api.nvim__id_float({[vim.type_idx]=vim.types.dictionary})")]])))
  171. eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected lua table',
  172. remove_trace(exc_exec([[call luaeval("vim.api.nvim__id_array(1)")]])))
  173. eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Unexpected type',
  174. remove_trace(exc_exec([[call luaeval("vim.api.nvim__id_array({[vim.type_idx]=vim.types.dictionary})")]])))
  175. eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected lua table',
  176. remove_trace(exc_exec([[call luaeval("vim.api.nvim__id_dictionary(1)")]])))
  177. eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Unexpected type',
  178. remove_trace(exc_exec([[call luaeval("vim.api.nvim__id_dictionary({[vim.type_idx]=vim.types.array})")]])))
  179. eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected lua table',
  180. remove_trace(exc_exec([[call luaeval("vim.api.nvim_set_keymap('', '', '', '')")]])))
  181. -- TODO: check for errors with Tabpage argument
  182. -- TODO: check for errors with Window argument
  183. -- TODO: check for errors with Buffer argument
  184. end)
  185. it('accepts any value as API Boolean', function()
  186. eq('', funcs.luaeval('vim.api.nvim_replace_termcodes("", vim, false, nil)'))
  187. eq('', funcs.luaeval('vim.api.nvim_replace_termcodes("", 0, 1.5, "test")'))
  188. eq('', funcs.luaeval('vim.api.nvim_replace_termcodes("", true, {}, {[vim.type_idx]=vim.types.array})'))
  189. end)
  190. end)