neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

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)