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