termxx_spec.lua 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. local t = require('test.testutil')
  2. local n = require('test.functional.testnvim')()
  3. local tt = require('test.functional.testterm')
  4. local uv = vim.uv
  5. local clear, command, testprg = n.clear, n.command, n.testprg
  6. local eval, eq, neq, retry = n.eval, t.eq, t.neq, t.retry
  7. local matches = t.matches
  8. local ok = t.ok
  9. local feed = n.feed
  10. local api = n.api
  11. local pcall_err = t.pcall_err
  12. local assert_alive = n.assert_alive
  13. local skip = t.skip
  14. local is_os = t.is_os
  15. describe('autocmd TermClose', function()
  16. before_each(function()
  17. clear()
  18. api.nvim_set_option_value('shell', testprg('shell-test'), {})
  19. command('set shellcmdflag=EXE shellredir= shellpipe= shellquote= shellxquote=')
  20. end)
  21. local function test_termclose_delete_own_buf()
  22. -- The terminal process needs to keep running so that TermClose isn't triggered immediately.
  23. api.nvim_set_option_value('shell', string.format('"%s" INTERACT', testprg('shell-test')), {})
  24. command('autocmd TermClose * bdelete!')
  25. command('terminal')
  26. matches(
  27. '^TermClose Autocommands for "%*": Vim%(bdelete%):E937: Attempt to delete a buffer that is in use: term://',
  28. pcall_err(command, 'bdelete!')
  29. )
  30. assert_alive()
  31. end
  32. -- TODO: fixed after merging patches for `can_unload_buffer`?
  33. pending('TermClose deleting its own buffer, altbuf = buffer 1 #10386', function()
  34. test_termclose_delete_own_buf()
  35. end)
  36. it('TermClose deleting its own buffer, altbuf NOT buffer 1 #10386', function()
  37. command('edit foo1')
  38. test_termclose_delete_own_buf()
  39. end)
  40. it('triggers when fast-exiting terminal job stops', function()
  41. command('autocmd TermClose * let g:test_termclose = 23')
  42. command('terminal')
  43. -- shell-test exits immediately.
  44. retry(nil, nil, function()
  45. neq(-1, eval('jobwait([&channel], 0)[0]'))
  46. end)
  47. retry(nil, nil, function()
  48. eq(23, eval('g:test_termclose'))
  49. end)
  50. end)
  51. it('triggers when long-running terminal job gets stopped', function()
  52. skip(is_os('win'))
  53. api.nvim_set_option_value('shell', is_os('win') and 'cmd.exe' or 'sh', {})
  54. command('autocmd TermClose * let g:test_termclose = 23')
  55. command('terminal')
  56. command('call jobstop(b:terminal_job_id)')
  57. retry(nil, nil, function()
  58. eq(23, eval('g:test_termclose'))
  59. end)
  60. end)
  61. it('kills job trapping SIGTERM', function()
  62. skip(is_os('win'))
  63. api.nvim_set_option_value('shell', 'sh', {})
  64. api.nvim_set_option_value('shellcmdflag', '-c', {})
  65. command(
  66. [[ let g:test_job = jobstart('trap "" TERM && echo 1 && sleep 60', { ]]
  67. .. [[ 'on_stdout': {-> execute('let g:test_job_started = 1')}, ]]
  68. .. [[ 'on_exit': {-> execute('let g:test_job_exited = 1')}}) ]]
  69. )
  70. retry(nil, nil, function()
  71. eq(1, eval('get(g:, "test_job_started", 0)'))
  72. end)
  73. uv.update_time()
  74. local start = uv.now()
  75. command('call jobstop(g:test_job)')
  76. retry(nil, nil, function()
  77. eq(1, eval('get(g:, "test_job_exited", 0)'))
  78. end)
  79. uv.update_time()
  80. local duration = uv.now() - start
  81. -- Nvim begins SIGTERM after KILL_TIMEOUT_MS.
  82. ok(duration >= 2000)
  83. ok(duration <= 4000) -- Epsilon for slow CI
  84. end)
  85. it('kills PTY job trapping SIGHUP and SIGTERM', function()
  86. skip(is_os('win'))
  87. api.nvim_set_option_value('shell', 'sh', {})
  88. api.nvim_set_option_value('shellcmdflag', '-c', {})
  89. command(
  90. [[ let g:test_job = jobstart('trap "" HUP TERM && echo 1 && sleep 60', { ]]
  91. .. [[ 'pty': 1,]]
  92. .. [[ 'on_stdout': {-> execute('let g:test_job_started = 1')}, ]]
  93. .. [[ 'on_exit': {-> execute('let g:test_job_exited = 1')}}) ]]
  94. )
  95. retry(nil, nil, function()
  96. eq(1, eval('get(g:, "test_job_started", 0)'))
  97. end)
  98. uv.update_time()
  99. local start = uv.now()
  100. command('call jobstop(g:test_job)')
  101. retry(nil, nil, function()
  102. eq(1, eval('get(g:, "test_job_exited", 0)'))
  103. end)
  104. uv.update_time()
  105. local duration = uv.now() - start
  106. -- Nvim begins SIGKILL after (2 * KILL_TIMEOUT_MS).
  107. ok(duration >= 4000)
  108. ok(duration <= 7000) -- Epsilon for slow CI
  109. end)
  110. it('reports the correct <abuf>', function()
  111. command('set hidden')
  112. command('set shellcmdflag=EXE')
  113. command('autocmd TermClose * let g:abuf = expand("<abuf>")')
  114. command('edit foo')
  115. command('edit bar')
  116. eq(2, eval('bufnr("%")'))
  117. command('terminal ls')
  118. retry(nil, nil, function()
  119. eq(3, eval('bufnr("%")'))
  120. end)
  121. command('buffer 1')
  122. retry(nil, nil, function()
  123. eq(1, eval('bufnr("%")'))
  124. end)
  125. command('3bdelete!')
  126. retry(nil, nil, function()
  127. eq('3', eval('g:abuf'))
  128. end)
  129. feed('<c-c>:qa!<cr>')
  130. end)
  131. it('exposes v:event.status', function()
  132. command('set shellcmdflag=EXIT')
  133. command('autocmd TermClose * let g:status = v:event.status')
  134. command('terminal 0')
  135. retry(nil, nil, function()
  136. eq(0, eval('g:status'))
  137. end)
  138. command('terminal 42')
  139. retry(nil, nil, function()
  140. eq(42, eval('g:status'))
  141. end)
  142. end)
  143. end)
  144. it('autocmd TermEnter, TermLeave', function()
  145. clear()
  146. command('let g:evs = []')
  147. command('autocmd TermOpen * call add(g:evs, ["TermOpen", mode()])')
  148. command('autocmd TermClose * call add(g:evs, ["TermClose", mode()])')
  149. command('autocmd TermEnter * call add(g:evs, ["TermEnter", mode()])')
  150. command('autocmd TermLeave * call add(g:evs, ["TermLeave", mode()])')
  151. command('terminal')
  152. feed('i')
  153. eq({ { 'TermOpen', 'n' }, { 'TermEnter', 't' } }, eval('g:evs'))
  154. feed([[<C-\><C-n>]])
  155. feed('A')
  156. eq(
  157. { { 'TermOpen', 'n' }, { 'TermEnter', 't' }, { 'TermLeave', 'n' }, { 'TermEnter', 't' } },
  158. eval('g:evs')
  159. )
  160. -- TermLeave is also triggered by :quit.
  161. command('split foo')
  162. feed('<Ignore>') -- Add input to separate two RPC requests
  163. command('wincmd w')
  164. feed('i')
  165. command('q!')
  166. feed('<Ignore>') -- Add input to separate two RPC requests
  167. eq({
  168. { 'TermOpen', 'n' },
  169. { 'TermEnter', 't' },
  170. { 'TermLeave', 'n' },
  171. { 'TermEnter', 't' },
  172. { 'TermLeave', 'n' },
  173. { 'TermEnter', 't' },
  174. { 'TermClose', 't' },
  175. { 'TermLeave', 'n' },
  176. }, eval('g:evs'))
  177. end)
  178. describe('autocmd TextChangedT', function()
  179. local screen
  180. before_each(function()
  181. clear()
  182. screen = tt.setup_screen()
  183. end)
  184. it('works', function()
  185. command('autocmd TextChangedT * ++once let g:called = 1')
  186. tt.feed_data('a')
  187. retry(nil, nil, function()
  188. eq(1, api.nvim_get_var('called'))
  189. end)
  190. end)
  191. it('cannot delete terminal buffer', function()
  192. command([[autocmd TextChangedT * call nvim_input('<CR>') | bwipe!]])
  193. tt.feed_data('a')
  194. screen:expect({ any = 'E937: ' })
  195. matches(
  196. '^E937: Attempt to delete a buffer that is in use: term://',
  197. api.nvim_get_vvar('errmsg')
  198. )
  199. end)
  200. end)