thread_spec.lua 12 KB

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