neovim

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

timer_spec.lua (8975B)


      1 local t = require('test.testutil')
      2 local n = require('test.functional.testnvim')()
      3 local Screen = require('test.functional.ui.screen')
      4 
      5 local feed, eq, eval, ok = n.feed, t.eq, n.eval, t.ok
      6 local source, async_meths, run = n.source, n.async_meths, n.run
      7 local clear, command, fn = n.clear, n.command, n.fn
      8 local exc_exec = n.exc_exec
      9 local api = n.api
     10 local load_adjust = n.load_adjust
     11 local retry = t.retry
     12 
     13 describe('timers', function()
     14  before_each(function()
     15    clear()
     16    source([[
     17      let g:val = 0
     18      func MyHandler(timer)
     19        let g:val += 1
     20      endfunc
     21    ]])
     22  end)
     23 
     24  it('works one-shot', function()
     25    eq(0, eval("[timer_start(10, 'MyHandler'), g:val][1]"))
     26    run(nil, nil, nil, load_adjust(100))
     27    eq(1, eval('g:val'))
     28  end)
     29 
     30  it('works one-shot when repeat=0', function()
     31    eq(0, eval("[timer_start(10, 'MyHandler', {'repeat': 0}), g:val][1]"))
     32    run(nil, nil, nil, load_adjust(100))
     33    eq(1, eval('g:val'))
     34  end)
     35 
     36  it('works with repeat two', function()
     37    eq(0, eval("[timer_start(10, 'MyHandler', {'repeat': 2}), g:val][1]"))
     38    run(nil, nil, nil, load_adjust(20))
     39    retry(nil, load_adjust(300), function()
     40      eq(2, eval('g:val'))
     41    end)
     42  end)
     43 
     44  it('are triggered during sleep', function()
     45    source([[
     46      let g:val = -1
     47      func! MyHandler(timer)
     48        if g:val >= 0
     49          let g:val += 1
     50          if g:val == 2
     51            call timer_stop(a:timer)
     52          endif
     53        endif
     54      endfunc
     55    ]])
     56    eval("timer_start(10, 'MyHandler', {'repeat': -1})")
     57    async_meths.nvim_command('sleep 10')
     58    eq(-1, eval('g:val')) -- timer did nothing yet.
     59    async_meths.nvim_command('let g:val = 0')
     60    run(nil, nil, nil, load_adjust(20))
     61    retry(nil, nil, function()
     62      eq(2, eval('g:val'))
     63    end)
     64  end)
     65 
     66  it('works with zero timeout', function()
     67    -- timer_start does still not invoke the callback immediately
     68    eq(0, eval("[timer_start(0, 'MyHandler', {'repeat': 1000}), g:val][1]"))
     69    retry(nil, nil, function()
     70      eq(1000, eval('g:val'))
     71    end)
     72  end)
     73 
     74  it('can be started during sleep', function()
     75    async_meths.nvim_command('sleep 10')
     76    -- this also tests that remote requests works during sleep
     77    eq(0, eval("[timer_start(10, 'MyHandler', {'repeat': 2}), g:val][1]"))
     78    run(nil, nil, nil, load_adjust(20))
     79    retry(nil, load_adjust(300), function()
     80      eq(2, eval('g:val'))
     81    end)
     82  end)
     83 
     84  it('are paused when event processing is disabled', function()
     85    command("call timer_start(5, 'MyHandler', {'repeat': -1})")
     86    run(nil, nil, nil, load_adjust(10))
     87    local count = eval('g:val')
     88    -- shows two line error message and thus invokes the return prompt.
     89    -- if we start to allow event processing here, we need to change this test.
     90    feed(':throw "fatal error"<CR>')
     91    run(nil, nil, nil, load_adjust(30))
     92    feed('<cr>')
     93    local diff = eval('g:val') - count
     94    assert(0 <= diff and diff <= 4, 'expected (0 <= diff <= 4), got: ' .. tostring(diff))
     95  end)
     96 
     97  it('are triggered in inputlist() call #7857', function()
     98    async_meths.nvim_exec2(
     99      [[
    100        call timer_start(5, 'MyHandler', {'repeat': -1})
    101        let g:val = 0
    102        let g:n = inputlist(['input0', 'input1'])
    103      ]],
    104      {}
    105    )
    106    retry(nil, nil, function()
    107      local val = eval('g:val')
    108      ok(val >= 2, '>= 2', tostring(val))
    109      eq(0, eval("exists('g:n')"))
    110    end)
    111    feed('42<CR>')
    112    eq(42, eval('g:n'))
    113  end)
    114 
    115  it('are triggered in confirm() call', function()
    116    api.nvim_ui_attach(80, 24, {}) -- needed for confirm() to work
    117    async_meths.nvim_exec2(
    118      [[
    119        call timer_start(5, 'MyHandler', {'repeat': -1})
    120        let g:val = 0
    121        let g:n = confirm('Are you sure?', "&Yes\n&No\n&Cancel")
    122      ]],
    123      {}
    124    )
    125    retry(nil, nil, function()
    126      local val = eval('g:val')
    127      ok(val >= 2, '>= 2', tostring(val))
    128      eq(0, eval("exists('g:n')"))
    129    end)
    130    feed('c')
    131    eq(3, eval('g:n'))
    132  end)
    133 
    134  it('are triggered in blocking getchar() call', function()
    135    async_meths.nvim_exec2(
    136      [[
    137        call timer_start(5, 'MyHandler', {'repeat': -1})
    138        let g:val = 0
    139        let g:c = getchar()
    140      ]],
    141      {}
    142    )
    143    retry(nil, nil, function()
    144      local val = eval('g:val')
    145      ok(val >= 2, '>= 2', tostring(val))
    146      eq(0, eval("exists('g:c')"))
    147      eq(0, eval('getchar(1)'))
    148    end)
    149    feed('c')
    150    eq(99, eval('g:c'))
    151  end)
    152 
    153  it('can invoke redraw in blocking getchar() call', function()
    154    local screen = Screen.new(40, 6)
    155 
    156    api.nvim_buf_set_lines(0, 0, -1, true, { 'ITEM 1', 'ITEM 2' })
    157    source([[
    158      let g:cont = 0
    159      func! AddItem(timer)
    160        if !g:cont
    161          return
    162        endif
    163        call timer_stop(a:timer)
    164 
    165        call nvim_buf_set_lines(0, 2, 2, v:true, ['ITEM 3'])
    166 
    167        " Meant to test for what Vim tests in Test_peek_and_get_char.
    168        call getchar(1)
    169 
    170        redraw
    171      endfunc
    172    ]])
    173    async_meths.nvim_command("let g:c2 = getchar(-1, {'cursor': 'msg'})")
    174    async_meths.nvim_command(
    175      'call timer_start(' .. load_adjust(100) .. ", 'AddItem', {'repeat': -1})"
    176    )
    177 
    178    screen:expect([[
    179      ITEM 1                                  |
    180      ITEM 2                                  |
    181      {1:~                                       }|*3
    182      ^                                        |
    183    ]])
    184    async_meths.nvim_command('let g:cont = 1')
    185 
    186    screen:expect([[
    187      ITEM 1                                  |
    188      ITEM 2                                  |
    189      ITEM 3                                  |
    190      {1:~                                       }|*2
    191      ^                                        |
    192    ]])
    193 
    194    feed('3')
    195    eq(51, eval('g:c2'))
    196    screen:expect([[
    197      ^ITEM 1                                  |
    198      ITEM 2                                  |
    199      ITEM 3                                  |
    200      {1:~                                       }|*2
    201                                              |
    202    ]])
    203  end)
    204 
    205  it('can be stopped', function()
    206    local t_init_val = eval("[timer_start(5, 'MyHandler', {'repeat': -1}), g:val]")
    207    eq(0, t_init_val[2])
    208    run(nil, nil, nil, load_adjust(30))
    209    fn.timer_stop(t_init_val[1])
    210    local count = eval('g:val')
    211    run(nil, load_adjust(300), nil, load_adjust(30))
    212    local count2 = eval('g:val')
    213    -- when count is eval:ed after timer_stop this should be non-racy
    214    eq(count, count2)
    215  end)
    216 
    217  it('can be stopped from the handler', function()
    218    source([[
    219      func! MyHandler(timer)
    220        let g:val += 1
    221        if g:val == 3
    222          call timer_stop(a:timer)
    223          " check double stop is ignored
    224          call timer_stop(a:timer)
    225        endif
    226      endfunc
    227    ]])
    228    eq(0, eval('g:val'))
    229    command("call timer_start(10, 'MyHandler', {'repeat': -1})")
    230    retry(nil, nil, function()
    231      eq(3, eval('g:val'))
    232    end)
    233  end)
    234 
    235  it('can have two timers', function()
    236    source([[
    237      let g:val2 = 0
    238      func! MyHandler2(timer)
    239        let g:val2 += 1
    240      endfunc
    241    ]])
    242    command("call timer_start(2, 'MyHandler',  {'repeat': 3})")
    243    command("call timer_start(4, 'MyHandler2', {'repeat': 2})")
    244    retry(nil, nil, function()
    245      eq(3, eval('g:val'))
    246      eq(2, eval('g:val2'))
    247    end)
    248  end)
    249 
    250  it('do not crash when processing events in the handler', function()
    251    source([[
    252      let g:val = 0
    253      func! MyHandler(timer)
    254        call timer_stop(a:timer)
    255        sleep 10m
    256        let g:val += 1
    257      endfunc
    258    ]])
    259    command("call timer_start(5, 'MyHandler', {'repeat': 1})")
    260    run(nil, nil, nil, load_adjust(20))
    261    retry(nil, load_adjust(150), function()
    262      eq(1, eval('g:val'))
    263    end)
    264  end)
    265 
    266  it("doesn't mess up the cmdline", function()
    267    local screen = Screen.new(40, 6)
    268    source([[
    269      let g:val = 0
    270      func! MyHandler(timer)
    271        while !g:val
    272          return
    273        endwhile
    274        call timer_stop(a:timer)
    275 
    276        echo "evil"
    277        redraw
    278        let g:val = 2
    279      endfunc
    280    ]])
    281    command("call timer_start(100,  'MyHandler', {'repeat': -1})")
    282    feed(':good')
    283    screen:expect([[
    284                                              |
    285      {1:~                                       }|*4
    286      :good^                                   |
    287    ]])
    288    command('let g:val = 1')
    289    screen:expect_unchanged(true, load_adjust(200))
    290 
    291    eq(2, eval('g:val'))
    292  end)
    293 
    294  it("timer_start can't be used in the sandbox", function()
    295    source [[
    296      function! Scary(timer) abort
    297        call execute('echo ''execute() should be disallowed''', '')
    298      endfunction
    299    ]]
    300    eq('Vim(call):E48: Not allowed in sandbox', exc_exec("sandbox call timer_start(0, 'Scary')"))
    301  end)
    302 
    303  it('can be triggered after an empty string <expr> mapping #17257', function()
    304    local screen = Screen.new(40, 6)
    305    command([=[imap <expr> <F2> [timer_start(0, { _ -> execute("throw 'x'", "") }), ''][-1]]=])
    306    feed('i<F2>')
    307    screen:expect({ any = 'E605: Exception not caught: x' })
    308  end)
    309 end)