timer_spec.lua 7.8 KB

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