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)