embed_spec.lua 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. local t = require('test.testutil')
  2. local n = require('test.functional.testnvim')()
  3. local Screen = require('test.functional.ui.screen')
  4. local uv = vim.uv
  5. local api = n.api
  6. local feed = n.feed
  7. local eq = t.eq
  8. local neq = t.neq
  9. local clear = n.clear
  10. local ok = t.ok
  11. local fn = n.fn
  12. local nvim_prog = n.nvim_prog
  13. local retry = t.retry
  14. local write_file = t.write_file
  15. local assert_log = t.assert_log
  16. local check_close = n.check_close
  17. local is_os = t.is_os
  18. local testlog = 'Xtest-embed-log'
  19. local function test_embed(ext_linegrid)
  20. local screen
  21. local function startup(...)
  22. clear { args_rm = { '--headless' }, args = { ... } }
  23. -- attach immediately after startup, for early UI
  24. screen = Screen.new(60, 8, { ext_linegrid = ext_linegrid })
  25. screen:add_extra_attr_ids {
  26. [100] = { foreground = Screen.colors.NvimDarkCyan },
  27. [101] = { foreground = Screen.colors.NvimDarkRed },
  28. [102] = {
  29. background = Screen.colors.NvimDarkGrey3,
  30. foreground = Screen.colors.NvimLightGrey3,
  31. },
  32. }
  33. end
  34. it('can display errors', function()
  35. startup('--cmd', 'echoerr invalid+')
  36. screen:expect([[
  37. |*4
  38. {102: }|
  39. {9:Error detected while processing pre-vimrc command line:} |
  40. {9:E121: Undefined variable: invalid} |
  41. {6:Press ENTER or type command to continue}^ |
  42. ]])
  43. feed('<cr>')
  44. screen:expect([[
  45. ^ |
  46. {1:~ }|*6
  47. |
  48. ]])
  49. end)
  50. it("doesn't erase output when setting color scheme", function()
  51. if t.is_os('openbsd') then
  52. pending('FIXME #10804')
  53. end
  54. startup('--cmd', 'echoerr "foo"', '--cmd', 'color default', '--cmd', 'echoerr "bar"')
  55. screen:expect([[
  56. |*3
  57. {102: }|
  58. {9:Error detected while processing pre-vimrc command line:} |
  59. {9:foo} |
  60. {101:bar} |
  61. {100:Press ENTER or type command to continue}^ |
  62. ]])
  63. end)
  64. it("doesn't erase output when setting Normal colors", function()
  65. startup('--cmd', 'echoerr "foo"', '--cmd', 'hi Normal guibg=Green', '--cmd', 'echoerr "bar"')
  66. screen:expect {
  67. grid = [[
  68. |*3
  69. {102: }|
  70. {9:Error detected while processing pre-vimrc command line:} |
  71. {9:foo} |
  72. {9:bar} |
  73. {6:Press ENTER or type command to continue}^ |
  74. ]],
  75. condition = function()
  76. eq(Screen.colors.Green, screen.default_colors.rgb_bg)
  77. end,
  78. }
  79. end)
  80. end
  81. describe('--embed UI on startup (ext_linegrid=true)', function()
  82. test_embed(true)
  83. end)
  84. describe('--embed UI on startup (ext_linegrid=false)', function()
  85. test_embed(false)
  86. end)
  87. describe('--embed UI', function()
  88. after_each(function()
  89. check_close()
  90. os.remove(testlog)
  91. end)
  92. it('can pass stdin', function()
  93. local pipe = assert(uv.pipe())
  94. local writer = assert(uv.new_pipe(false))
  95. writer:open(pipe.write)
  96. clear { args_rm = { '--headless' }, io_extra = pipe.read, env = { NVIM_LOG_FILE = testlog } }
  97. -- attach immediately after startup, for early UI
  98. -- rpc_async: Avoid hanging. #24888
  99. local screen = Screen.new(40, 8, { stdin_fd = 3 }, false)
  100. screen.rpc_async = true -- Avoid hanging. #24888
  101. screen:attach()
  102. writer:write 'hello nvim\nfrom external input\n'
  103. writer:shutdown(function()
  104. writer:close()
  105. end)
  106. screen:expect [[
  107. ^hello nvim |
  108. from external input |
  109. {1:~ }|*5
  110. |
  111. ]]
  112. -- stdin (rpc input) still works
  113. feed 'o'
  114. screen:expect [[
  115. hello nvim |
  116. ^ |
  117. from external input |
  118. {1:~ }|*4
  119. {5:-- INSERT --} |
  120. ]]
  121. if not is_os('win') then
  122. assert_log('Failed to get flags on descriptor 3: Bad file descriptor', testlog, 100)
  123. end
  124. end)
  125. it('can pass stdin to -q - #17523', function()
  126. write_file(
  127. 'Xbadfile.c',
  128. [[
  129. /* some file with an error */
  130. main() {
  131. functionCall(arg; arg, arg);
  132. return 666
  133. }
  134. ]]
  135. )
  136. finally(function()
  137. os.remove('Xbadfile.c')
  138. end)
  139. local pipe = assert(uv.pipe())
  140. local writer = assert(uv.new_pipe(false))
  141. writer:open(pipe.write)
  142. clear { args_rm = { '--headless' }, args = { '-q', '-' }, io_extra = pipe.read }
  143. -- attach immediately after startup, for early UI
  144. local screen = Screen.new(60, 8, { stdin_fd = 3 }, false)
  145. screen.rpc_async = true -- Avoid hanging. #24888
  146. screen:attach()
  147. writer:write [[Xbadfile.c:4:12: error: expected ';' before '}' token]]
  148. writer:shutdown(function()
  149. writer:close()
  150. end)
  151. screen:expect [[
  152. /* some file with an error */ |
  153. main() { |
  154. functionCall(arg; arg, arg); |
  155. return 66^6 |
  156. } |
  157. {1:~ }|*2
  158. (1 of 1): error: expected ';' before '}' token |
  159. ]]
  160. -- stdin (rpc input) still works
  161. feed 'A'
  162. screen:expect [[
  163. /* some file with an error */ |
  164. main() { |
  165. functionCall(arg; arg, arg); |
  166. return 666^ |
  167. } |
  168. {1:~ }|*2
  169. {5:-- INSERT --} |
  170. ]]
  171. eq('-', api.nvim_get_option_value('errorfile', {}))
  172. end)
  173. it('only sets background colors once even if overridden', function()
  174. local screen, current, seen
  175. local function handle_default_colors_set(_, _, rgb_bg, _, _, _)
  176. seen[rgb_bg] = true
  177. current = rgb_bg
  178. end
  179. local function startup(...)
  180. seen = {}
  181. current = nil
  182. clear { args_rm = { '--headless' }, args = { ... } }
  183. -- attach immediately after startup, for early UI
  184. screen = Screen.new(40, 8)
  185. screen._handle_default_colors_set = handle_default_colors_set
  186. end
  187. startup()
  188. screen:expect {
  189. condition = function()
  190. eq(16777215, current)
  191. end,
  192. }
  193. eq({ [16777215] = true }, seen)
  194. -- NB: by accident how functional/testutil.lua currently handles the default color scheme, the
  195. -- above is sufficient to test the behavior. But in case that workaround is removed, we need
  196. -- a test with an explicit override like below, so do it to remain safe.
  197. startup('--cmd', 'hi NORMAL guibg=#FF00FF')
  198. screen:expect {
  199. condition = function()
  200. eq(16711935, current)
  201. end,
  202. }
  203. eq({ [16711935] = true }, seen) -- we only saw the last one, despite 16777215 was set internally earlier
  204. end)
  205. it('updates cwd of attached UI #21771', function()
  206. clear { args_rm = { '--headless' } }
  207. local screen = Screen.new(40, 8)
  208. screen:expect {
  209. condition = function()
  210. eq(t.paths.test_source_path, screen.pwd)
  211. end,
  212. }
  213. -- Change global cwd
  214. n.command(string.format('cd %s/src/nvim', t.paths.test_source_path))
  215. screen:expect {
  216. condition = function()
  217. eq(string.format('%s/src/nvim', t.paths.test_source_path), screen.pwd)
  218. end,
  219. }
  220. -- Split the window and change the cwd in the split
  221. n.command('new')
  222. n.command(string.format('lcd %s/test', t.paths.test_source_path))
  223. screen:expect {
  224. condition = function()
  225. eq(string.format('%s/test', t.paths.test_source_path), screen.pwd)
  226. end,
  227. }
  228. -- Move to the original window
  229. n.command('wincmd p')
  230. screen:expect {
  231. condition = function()
  232. eq(string.format('%s/src/nvim', t.paths.test_source_path), screen.pwd)
  233. end,
  234. }
  235. -- Change global cwd again
  236. n.command(string.format('cd %s', t.paths.test_source_path))
  237. screen:expect {
  238. condition = function()
  239. eq(t.paths.test_source_path, screen.pwd)
  240. end,
  241. }
  242. end)
  243. end)
  244. describe('--embed --listen UI', function()
  245. it('waits for connection on listening address', function()
  246. t.skip(t.is_os('win'))
  247. clear()
  248. local child_server = assert(n.new_pipename())
  249. fn.jobstart({
  250. nvim_prog,
  251. '--embed',
  252. '--listen',
  253. child_server,
  254. '--clean',
  255. '--cmd',
  256. 'colorscheme vim',
  257. })
  258. retry(nil, nil, function()
  259. neq(nil, uv.fs_stat(child_server))
  260. end)
  261. local child_session = n.connect(child_server)
  262. local info_ok, api_info = child_session:request('nvim_get_api_info')
  263. ok(info_ok)
  264. eq(2, #api_info)
  265. ok(api_info[1] > 2, 'channel_id > 2', api_info[1])
  266. child_session:request(
  267. 'nvim_exec2',
  268. [[
  269. let g:evs = []
  270. autocmd UIEnter * call add(g:evs, $"UIEnter:{v:event.chan}")
  271. autocmd VimEnter * call add(g:evs, "VimEnter")
  272. ]],
  273. {}
  274. )
  275. -- VimEnter and UIEnter shouldn't be triggered until after attach
  276. local var_ok, var = child_session:request('nvim_get_var', 'evs')
  277. ok(var_ok)
  278. eq({}, var)
  279. local child_screen = Screen.new(40, 6, nil, child_session)
  280. child_screen:expect {
  281. grid = [[
  282. ^ |
  283. {1:~ }|*3
  284. {2:[No Name] 0,0-1 All}|
  285. |
  286. ]],
  287. attr_ids = {
  288. [1] = { foreground = Screen.colors.Blue, bold = true },
  289. [2] = { reverse = true, bold = true },
  290. },
  291. }
  292. -- VimEnter and UIEnter should now be triggered
  293. var_ok, var = child_session:request('nvim_get_var', 'evs')
  294. ok(var_ok)
  295. eq({ 'VimEnter', ('UIEnter:%d'):format(api_info[1]) }, var)
  296. end)
  297. end)