thread_spec.lua 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. local t = require('test.testutil')
  2. local n = require('test.functional.testnvim')()
  3. local Screen = require('test.functional.ui.screen')
  4. local assert_alive = n.assert_alive
  5. local clear = n.clear
  6. local feed = n.feed
  7. local eq = t.eq
  8. local exec_lua = n.exec_lua
  9. local next_msg = n.next_msg
  10. local NIL = vim.NIL
  11. local pcall_err = t.pcall_err
  12. describe('thread', function()
  13. local screen
  14. before_each(function()
  15. clear()
  16. screen = Screen.new(50, 10)
  17. end)
  18. it('handle non-string error', function()
  19. exec_lua [[
  20. local thread = vim.uv.new_thread(function()
  21. error()
  22. end)
  23. vim.uv.thread_join(thread)
  24. ]]
  25. screen:expect([[
  26. |
  27. {1:~ }|*5
  28. {3: }|
  29. {9:Error in luv thread:} |
  30. {9:[NULL]} |
  31. {6:Press ENTER or type command to continue}^ |
  32. ]])
  33. feed('<cr>')
  34. assert_alive()
  35. end)
  36. it('entry func is executed in protected mode', function()
  37. exec_lua [[
  38. local thread = vim.uv.new_thread(function()
  39. error('Error in thread entry func')
  40. end)
  41. vim.uv.thread_join(thread)
  42. ]]
  43. screen:expect([[
  44. |
  45. {1:~ }|*5
  46. {3: }|
  47. {9:Error in luv thread:} |
  48. {9:[string "<nvim>"]:2: Error in thread entry func} |
  49. {6:Press ENTER or type command to continue}^ |
  50. ]])
  51. feed('<cr>')
  52. assert_alive()
  53. end)
  54. it('callback is executed in protected mode', function()
  55. exec_lua [[
  56. local thread = vim.uv.new_thread(function()
  57. local timer = vim.uv.new_timer()
  58. local function ontimeout()
  59. timer:stop()
  60. timer:close()
  61. error('Error in thread callback')
  62. end
  63. timer:start(10, 0, ontimeout)
  64. vim.uv.run()
  65. end)
  66. vim.uv.thread_join(thread)
  67. ]]
  68. screen:expect([[
  69. |
  70. {1:~ }|*5
  71. {3: }|
  72. {9:Error in luv callback, thread:} |
  73. {9:[string "<nvim>"]:6: Error in thread callback} |
  74. {6:Press ENTER or type command to continue}^ |
  75. ]])
  76. feed('<cr>')
  77. assert_alive()
  78. end)
  79. describe('print', function()
  80. it('works', function()
  81. exec_lua [[
  82. local thread = vim.uv.new_thread(function()
  83. print('print in thread')
  84. end)
  85. vim.uv.thread_join(thread)
  86. ]]
  87. screen:expect([[
  88. ^ |
  89. {1:~ }|*8
  90. print in thread |
  91. ]])
  92. end)
  93. it('vim.inspect', function()
  94. exec_lua [[
  95. local thread = vim.uv.new_thread(function()
  96. print(vim.inspect({1,2}))
  97. end)
  98. vim.uv.thread_join(thread)
  99. ]]
  100. screen:expect([[
  101. ^ |
  102. {1:~ }|*8
  103. { 1, 2 } |
  104. ]])
  105. end)
  106. end)
  107. describe('vim.*', function()
  108. before_each(function()
  109. clear()
  110. exec_lua [[
  111. Thread_Test = {}
  112. Thread_Test.entry_func = function(async, entry_str, args)
  113. local decoded_args = vim.mpack.decode(args)
  114. assert(loadstring(entry_str))(async, decoded_args)
  115. end
  116. function Thread_Test:do_test()
  117. local async
  118. local on_async = self.on_async
  119. async = vim.uv.new_async(function(ret)
  120. on_async(ret)
  121. async:close()
  122. end)
  123. local thread =
  124. vim.uv.new_thread(self.entry_func, async, self.entry_str, self.args)
  125. vim.uv.thread_join(thread)
  126. end
  127. Thread_Test.new = function(entry, on_async, ...)
  128. self = {}
  129. setmetatable(self, {__index = Thread_Test})
  130. self.args = vim.mpack.encode({...})
  131. self.entry_str = string.dump(entry)
  132. self.on_async = on_async
  133. return self
  134. end
  135. ]]
  136. end)
  137. it('is_thread', function()
  138. exec_lua [[
  139. local entry = function(async)
  140. async:send(vim.is_thread())
  141. end
  142. local on_async = function(ret)
  143. vim.rpcnotify(1, 'result', ret)
  144. end
  145. local thread_test = Thread_Test.new(entry, on_async)
  146. thread_test:do_test()
  147. ]]
  148. eq({ 'notification', 'result', { true } }, next_msg())
  149. end)
  150. it('uv', function()
  151. exec_lua [[
  152. local entry = function(async)
  153. async:send(vim.uv.version())
  154. end
  155. local on_async = function(ret)
  156. vim.rpcnotify(1, ret)
  157. end
  158. local thread_test = Thread_Test.new(entry, on_async)
  159. thread_test:do_test()
  160. ]]
  161. local msg = next_msg()
  162. eq('notification', msg[1])
  163. assert(tonumber(msg[2]) >= 72961)
  164. end)
  165. it('mpack', function()
  166. exec_lua [[
  167. local entry = function(async)
  168. async:send(vim.mpack.encode({33, vim.NIL, 'text'}))
  169. end
  170. local on_async = function(ret)
  171. vim.rpcnotify(1, 'result', vim.mpack.decode(ret))
  172. end
  173. local thread_test = Thread_Test.new(entry, on_async)
  174. thread_test:do_test()
  175. ]]
  176. eq({ 'notification', 'result', { { 33, NIL, 'text' } } }, next_msg())
  177. end)
  178. it('json', function()
  179. exec_lua [[
  180. local entry = function(async)
  181. async:send(vim.json.encode({33, vim.NIL, 'text'}))
  182. end
  183. local on_async = function(ret)
  184. vim.rpcnotify(1, 'result', vim.json.decode(ret))
  185. end
  186. local thread_test = Thread_Test.new(entry, on_async)
  187. thread_test:do_test()
  188. ]]
  189. eq({ 'notification', 'result', { { 33, NIL, 'text' } } }, next_msg())
  190. end)
  191. it('diff', function()
  192. exec_lua [[
  193. local entry = function(async)
  194. async:send(vim.diff('Hello\n', 'Helli\n'))
  195. end
  196. local on_async = function(ret)
  197. vim.rpcnotify(1, 'result', ret)
  198. end
  199. local thread_test = Thread_Test.new(entry, on_async)
  200. thread_test:do_test()
  201. ]]
  202. eq({
  203. 'notification',
  204. 'result',
  205. {
  206. table.concat({
  207. '@@ -1 +1 @@',
  208. '-Hello',
  209. '+Helli',
  210. '',
  211. }, '\n'),
  212. },
  213. }, next_msg())
  214. end)
  215. end)
  216. end)
  217. describe('threadpool', function()
  218. before_each(clear)
  219. it('is_thread', function()
  220. eq(false, exec_lua [[return vim.is_thread()]])
  221. exec_lua [[
  222. local work_fn = function()
  223. return vim.is_thread()
  224. end
  225. local after_work_fn = function(ret)
  226. vim.rpcnotify(1, 'result', ret)
  227. end
  228. local work = vim.uv.new_work(work_fn, after_work_fn)
  229. work:queue()
  230. ]]
  231. eq({ 'notification', 'result', { true } }, next_msg())
  232. end)
  233. it('with invalid argument', function()
  234. local status = pcall_err(
  235. exec_lua,
  236. [[
  237. local work = vim.uv.new_thread(function() end, function() end)
  238. work:queue({})
  239. ]]
  240. )
  241. eq([[Error: thread arg not support type 'function' at 1]], status)
  242. end)
  243. it('with invalid return value', function()
  244. local screen = Screen.new(50, 10)
  245. exec_lua [[
  246. local work = vim.uv.new_work(function() return {} end, function() end)
  247. work:queue()
  248. ]]
  249. screen:expect([[
  250. |
  251. {1:~ }|*5
  252. {3: }|
  253. {9:Error in luv thread:} |
  254. {9:Error: thread arg not support type 'table' at 1} |
  255. {6:Press ENTER or type command to continue}^ |
  256. ]])
  257. end)
  258. describe('vim.*', function()
  259. before_each(function()
  260. clear()
  261. exec_lua [[
  262. Threadpool_Test = {}
  263. Threadpool_Test.work_fn = function(work_fn_str, args)
  264. local decoded_args = vim.mpack.decode(args)
  265. return assert(loadstring(work_fn_str))(decoded_args)
  266. end
  267. function Threadpool_Test:do_test()
  268. local work =
  269. vim.uv.new_work(self.work_fn, self.after_work)
  270. work:queue(self.work_fn_str, self.args)
  271. end
  272. Threadpool_Test.new = function(work_fn, after_work, ...)
  273. self = {}
  274. setmetatable(self, {__index = Threadpool_Test})
  275. self.args = vim.mpack.encode({...})
  276. self.work_fn_str = string.dump(work_fn)
  277. self.after_work = after_work
  278. return self
  279. end
  280. ]]
  281. end)
  282. it('uv', function()
  283. exec_lua [[
  284. local work_fn = function()
  285. return vim.uv.version()
  286. end
  287. local after_work_fn = function(ret)
  288. vim.rpcnotify(1, ret)
  289. end
  290. local threadpool_test = Threadpool_Test.new(work_fn, after_work_fn)
  291. threadpool_test:do_test()
  292. ]]
  293. local msg = next_msg()
  294. eq('notification', msg[1])
  295. assert(tonumber(msg[2]) >= 72961)
  296. end)
  297. it('mpack', function()
  298. exec_lua [[
  299. local work_fn = function()
  300. local var = vim.mpack.encode({33, vim.NIL, 'text'})
  301. return var
  302. end
  303. local after_work_fn = function(ret)
  304. vim.rpcnotify(1, 'result', vim.mpack.decode(ret))
  305. end
  306. local threadpool_test = Threadpool_Test.new(work_fn, after_work_fn)
  307. threadpool_test:do_test()
  308. ]]
  309. eq({ 'notification', 'result', { { 33, NIL, 'text' } } }, next_msg())
  310. end)
  311. it('json', function()
  312. exec_lua [[
  313. local work_fn = function()
  314. local var = vim.json.encode({33, vim.NIL, 'text'})
  315. return var
  316. end
  317. local after_work_fn = function(ret)
  318. vim.rpcnotify(1, 'result', vim.json.decode(ret))
  319. end
  320. local threadpool_test = Threadpool_Test.new(work_fn, after_work_fn)
  321. threadpool_test:do_test()
  322. ]]
  323. eq({ 'notification', 'result', { { 33, NIL, 'text' } } }, next_msg())
  324. end)
  325. it('work', function()
  326. exec_lua [[
  327. local work_fn = function()
  328. return vim.diff('Hello\n', 'Helli\n')
  329. end
  330. local after_work_fn = function(ret)
  331. vim.rpcnotify(1, 'result', ret)
  332. end
  333. local threadpool_test = Threadpool_Test.new(work_fn, after_work_fn)
  334. threadpool_test:do_test()
  335. ]]
  336. eq({
  337. 'notification',
  338. 'result',
  339. {
  340. table.concat({
  341. '@@ -1 +1 @@',
  342. '-Hello',
  343. '+Helli',
  344. '',
  345. }, '\n'),
  346. },
  347. }, next_msg())
  348. end)
  349. end)
  350. end)