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