1" Test for channel and job functions. 2 3" When +channel is supported then +job is too, so we don't check for that. 4source check.vim 5CheckFeature channel 6 7source shared.vim 8source screendump.vim 9source view_util.vim 10 11let s:python = PythonProg() 12if s:python == '' 13 " Can't run this test without Python. 14 throw 'Skipped: Python command missing' 15endif 16 17" Uncomment the next line to see what happens. Output is in 18" src/testdir/channellog. 19" Add ch_log() calls where you want to see what happens. 20" call ch_logfile('channellog', 'w') 21 22func SetUp() 23 if g:testfunc =~ '_ipv6()$' 24 let s:localhost = '[::1]:' 25 let s:testscript = 'test_channel_6.py' 26 else 27 let s:localhost = 'localhost:' 28 let s:testscript = 'test_channel.py' 29 endif 30 let s:chopt = {} 31 call ch_log(g:testfunc) 32 33 " Most tests use job_start(), which can be flaky 34 let g:test_is_flaky = 1 35endfunc 36 37" Run "testfunc" after starting the server and stop the server afterwards. 38func s:run_server(testfunc, ...) 39 call RunServer(s:testscript, a:testfunc, a:000) 40endfunc 41 42" Return a list of open files. 43" Can be used to make sure no resources leaked. 44" Returns an empty list on systems where this is not supported. 45func s:get_resources() 46 let pid = getpid() 47 48 if executable('lsof') 49 return systemlist('lsof -p ' . pid . ' | awk ''$4~/^[0-9]*[rwu]$/&&$5=="REG"{print$NF}''') 50 elseif isdirectory('/proc/' . pid . '/fd/') 51 return systemlist('readlink /proc/' . pid . '/fd/* | grep -v ''^/dev/''') 52 else 53 return [] 54 endif 55endfunc 56 57let g:Ch_responseMsg = '' 58func Ch_requestHandler(handle, msg) 59 let g:Ch_responseHandle = a:handle 60 let g:Ch_responseMsg = a:msg 61endfunc 62 63func Ch_communicate(port) 64 " Avoid dropping messages, since we don't use a callback here. 65 let s:chopt.drop = 'never' 66 " Also add the noblock flag to try it out. 67 let s:chopt.noblock = 1 68 let handle = ch_open(s:localhost . a:port, s:chopt) 69 if ch_status(handle) == "fail" 70 call assert_report("Can't open channel") 71 return 72 endif 73 74 " check that getjob without a job is handled correctly 75 call assert_equal('no process', string(ch_getjob(handle))) 76 77 let dict = handle->ch_info() 78 call assert_true(dict.id != 0) 79 call assert_equal('open', dict.status) 80 call assert_equal(a:port, string(dict.port)) 81 call assert_equal('open', dict.sock_status) 82 call assert_equal('socket', dict.sock_io) 83 84 " Simple string request and reply. 85 call assert_equal('got it', ch_evalexpr(handle, 'hello!')) 86 87 " Malformed command should be ignored. 88 call assert_equal('ok', ch_evalexpr(handle, 'malformed1')) 89 call assert_equal('ok', ch_evalexpr(handle, 'malformed2')) 90 call assert_equal('ok', ch_evalexpr(handle, 'malformed3')) 91 92 " split command should work 93 call assert_equal('ok', ch_evalexpr(handle, 'split')) 94 call WaitFor('exists("g:split")') 95 call assert_equal(123, g:split) 96 97 " string with ][ should work 98 call assert_equal('this][that', ch_evalexpr(handle, 'echo this][that')) 99 100 " nothing to read now 101 call assert_equal(0, ch_canread(handle)) 102 103 " sending three messages quickly then reading should work 104 for i in range(3) 105 call ch_sendexpr(handle, 'echo hello ' . i) 106 endfor 107 call assert_equal('hello 0', ch_read(handle)[1]) 108 call assert_equal('hello 1', ch_read(handle)[1]) 109 call assert_equal('hello 2', ch_read(handle)[1]) 110 111 " Request that triggers sending two ex commands. These will usually be 112 " handled before getting the response, but it's not guaranteed, thus wait a 113 " tiny bit for the commands to get executed. 114 call assert_equal('ok', ch_evalexpr(handle, 'make change')) 115 call WaitForAssert({-> assert_equal("added2", getline("$"))}) 116 call assert_equal('added1', getline(line('$') - 1)) 117 118 " Request command "echoerr 'this is an error'". 119 " This will throw an exception, catch it here. 120 let caught = 'no' 121 try 122 call assert_equal('ok', ch_evalexpr(handle, 'echoerr')) 123 catch /this is an error/ 124 let caught = 'yes' 125 endtry 126 if caught != 'yes' 127 call assert_report("Expected exception from error message") 128 endif 129 130 " Request command "foo bar", which fails silently. 131 call assert_equal('ok', ch_evalexpr(handle, 'bad command')) 132 call WaitForAssert({-> assert_match("E492:.*foo bar", v:errmsg)}) 133 134 call assert_equal('ok', ch_evalexpr(handle, 'do normal', {'timeout': 100})) 135 call WaitForAssert({-> assert_equal('added more', getline('$'))}) 136 137 " Send a request with a specific handler. 138 call ch_sendexpr(handle, 'hello!', {'callback': 'Ch_requestHandler'}) 139 call WaitFor('exists("g:Ch_responseHandle")') 140 if !exists('g:Ch_responseHandle') 141 call assert_report('g:Ch_responseHandle was not set') 142 else 143 call assert_equal(handle, g:Ch_responseHandle) 144 unlet g:Ch_responseHandle 145 endif 146 call assert_equal('got it', g:Ch_responseMsg) 147 148 let g:Ch_responseMsg = '' 149 call ch_sendexpr(handle, 'hello!', {'callback': function('Ch_requestHandler')}) 150 call WaitFor('exists("g:Ch_responseHandle")') 151 if !exists('g:Ch_responseHandle') 152 call assert_report('g:Ch_responseHandle was not set') 153 else 154 call assert_equal(handle, g:Ch_responseHandle) 155 unlet g:Ch_responseHandle 156 endif 157 call assert_equal('got it', g:Ch_responseMsg) 158 159 " Using lambda. 160 let g:Ch_responseMsg = '' 161 call ch_sendexpr(handle, 'hello!', {'callback': {a, b -> Ch_requestHandler(a, b)}}) 162 call WaitFor('exists("g:Ch_responseHandle")') 163 if !exists('g:Ch_responseHandle') 164 call assert_report('g:Ch_responseHandle was not set') 165 else 166 call assert_equal(handle, g:Ch_responseHandle) 167 unlet g:Ch_responseHandle 168 endif 169 call assert_equal('got it', g:Ch_responseMsg) 170 171 " Collect garbage, tests that our handle isn't collected. 172 call test_garbagecollect_now() 173 174 " check setting options (without testing the effect) 175 eval handle->ch_setoptions({'callback': 's:NotUsed'}) 176 call ch_setoptions(handle, {'timeout': 1111}) 177 call ch_setoptions(handle, {'mode': 'json'}) 178 call assert_fails("call ch_setoptions(handle, {'waittime': 111})", 'E475:') 179 call ch_setoptions(handle, {'callback': ''}) 180 call ch_setoptions(handle, {'drop': 'never'}) 181 call ch_setoptions(handle, {'drop': 'auto'}) 182 call assert_fails("call ch_setoptions(handle, {'drop': 'bad'})", 'E475:') 183 call assert_equal(0, ch_setoptions(handle, test_null_dict())) 184 call assert_equal(0, ch_setoptions(test_null_channel(), {'drop' : 'never'})) 185 186 " Send an eval request that works. 187 call assert_equal('ok', ch_evalexpr(handle, 'eval-works')) 188 sleep 10m 189 call assert_equal([-1, 'foo123'], ch_evalexpr(handle, 'eval-result')) 190 191 " Send an eval request with special characters. 192 call assert_equal('ok', ch_evalexpr(handle, 'eval-special')) 193 sleep 10m 194 call assert_equal([-2, "foo\x7f\x10\x01bar"], ch_evalexpr(handle, 'eval-result')) 195 196 " Send an eval request to get a line with special characters. 197 call setline(3, "a\nb\<CR>c\x01d\x7fe") 198 call assert_equal('ok', ch_evalexpr(handle, 'eval-getline')) 199 sleep 10m 200 call assert_equal([-3, "a\nb\<CR>c\x01d\x7fe"], ch_evalexpr(handle, 'eval-result')) 201 202 " Send an eval request that fails. 203 call assert_equal('ok', ch_evalexpr(handle, 'eval-fails')) 204 sleep 10m 205 call assert_equal([-4, 'ERROR'], ch_evalexpr(handle, 'eval-result')) 206 207 " Send an eval request that works but can't be encoded. 208 call assert_equal('ok', ch_evalexpr(handle, 'eval-error')) 209 sleep 10m 210 call assert_equal([-5, 'ERROR'], ch_evalexpr(handle, 'eval-result')) 211 212 " Send a bad eval request. There will be no response. 213 call assert_equal('ok', ch_evalexpr(handle, 'eval-bad')) 214 sleep 10m 215 call assert_equal([-5, 'ERROR'], ch_evalexpr(handle, 'eval-result')) 216 217 " Send an expr request 218 call assert_equal('ok', ch_evalexpr(handle, 'an expr')) 219 call WaitForAssert({-> assert_equal('three', getline('$'))}) 220 call assert_equal('one', getline(line('$') - 2)) 221 call assert_equal('two', getline(line('$') - 1)) 222 223 " Request a redraw, we don't check for the effect. 224 call assert_equal('ok', ch_evalexpr(handle, 'redraw')) 225 call assert_equal('ok', ch_evalexpr(handle, 'redraw!')) 226 227 call assert_equal('ok', ch_evalexpr(handle, 'empty-request')) 228 229 " Reading while there is nothing available. 230 call assert_equal(v:none, ch_read(handle, {'timeout': 0})) 231 if exists('*reltimefloat') 232 let start = reltime() 233 call assert_equal(v:none, ch_read(handle, {'timeout': 333})) 234 let elapsed = reltime(start) 235 call assert_inrange(0.3, 0.6, reltimefloat(reltime(start))) 236 endif 237 238 " Send without waiting for a response, then wait for a response. 239 call ch_sendexpr(handle, 'wait a bit') 240 let resp = ch_read(handle) 241 call assert_equal(type([]), type(resp)) 242 call assert_equal(type(11), type(resp[0])) 243 call assert_equal('waited', resp[1]) 244 245 " make the server quit, can't check if this works, should not hang. 246 call ch_sendexpr(handle, '!quit!') 247endfunc 248 249func Test_communicate() 250 call s:run_server('Ch_communicate') 251endfunc 252 253func Test_communicate_ipv6() 254 CheckIPv6 255 256 call Test_communicate() 257endfunc 258 259" Test that we can open two channels. 260func Ch_two_channels(port) 261 let handle = ch_open(s:localhost . a:port, s:chopt) 262 call assert_equal(v:t_channel, type(handle)) 263 if handle->ch_status() == "fail" 264 call assert_report("Can't open channel") 265 return 266 endif 267 268 call assert_equal('got it', ch_evalexpr(handle, 'hello!')) 269 270 let newhandle = ch_open(s:localhost . a:port, s:chopt) 271 if ch_status(newhandle) == "fail" 272 call assert_report("Can't open second channel") 273 return 274 endif 275 call assert_equal('got it', ch_evalexpr(newhandle, 'hello!')) 276 call assert_equal('got it', ch_evalexpr(handle, 'hello!')) 277 278 call ch_close(handle) 279 call assert_equal('got it', ch_evalexpr(newhandle, 'hello!')) 280 281 call ch_close(newhandle) 282 call assert_fails("call ch_close(newhandle)", 'E906:') 283endfunc 284 285func Test_two_channels() 286 eval 'Test_two_channels()'->ch_log() 287 call s:run_server('Ch_two_channels') 288endfunc 289 290func Test_two_channels_ipv6() 291 CheckIPv6 292 call Test_two_channels() 293endfunc 294 295" Test that a server crash is handled gracefully. 296func Ch_server_crash(port) 297 let handle = ch_open(s:localhost . a:port, s:chopt) 298 if ch_status(handle) == "fail" 299 call assert_report("Can't open channel") 300 return 301 endif 302 303 call ch_evalexpr(handle, '!crash!') 304 305 sleep 10m 306endfunc 307 308func Test_server_crash() 309 call s:run_server('Ch_server_crash') 310endfunc 311 312func Test_server_crash_ipv6() 313 CheckIPv6 314 call Test_server_crash() 315endfunc 316 317""""""""" 318 319func Ch_handler(chan, msg) 320 call ch_log('Ch_handler()') 321 unlet g:Ch_reply 322 let g:Ch_reply = a:msg 323endfunc 324 325func Ch_channel_handler(port) 326 let handle = ch_open(s:localhost . a:port, s:chopt) 327 if ch_status(handle) == "fail" 328 call assert_report("Can't open channel") 329 return 330 endif 331 332 " Test that it works while waiting on a numbered message. 333 call assert_equal('ok', ch_evalexpr(handle, 'call me')) 334 call WaitForAssert({-> assert_equal('we called you', g:Ch_reply)}) 335 336 " Test that it works while not waiting on a numbered message. 337 call ch_sendexpr(handle, 'call me again') 338 call WaitForAssert({-> assert_equal('we did call you', g:Ch_reply)}) 339endfunc 340 341func Test_channel_handler() 342 let g:Ch_reply = "" 343 let s:chopt.callback = 'Ch_handler' 344 call s:run_server('Ch_channel_handler') 345 let g:Ch_reply = "" 346 let s:chopt.callback = function('Ch_handler') 347 call s:run_server('Ch_channel_handler') 348endfunc 349 350func Test_channel_handler_ipv6() 351 CheckIPv6 352 call Test_channel_handler() 353endfunc 354 355""""""""" 356 357let g:Ch_reply = '' 358func Ch_zeroHandler(chan, msg) 359 unlet g:Ch_reply 360 let g:Ch_reply = a:msg 361endfunc 362 363let g:Ch_zero_reply = '' 364func Ch_oneHandler(chan, msg) 365 unlet g:Ch_zero_reply 366 let g:Ch_zero_reply = a:msg 367endfunc 368 369func Ch_channel_zero(port) 370 let handle = (s:localhost .. a:port)->ch_open(s:chopt) 371 if ch_status(handle) == "fail" 372 call assert_report("Can't open channel") 373 return 374 endif 375 376 " Check that eval works. 377 call assert_equal('got it', ch_evalexpr(handle, 'hello!')) 378 379 " Check that eval works if a zero id message is sent back. 380 let g:Ch_reply = '' 381 call assert_equal('sent zero', ch_evalexpr(handle, 'send zero')) 382 if s:has_handler 383 call WaitForAssert({-> assert_equal('zero index', g:Ch_reply)}) 384 else 385 sleep 20m 386 call assert_equal('', g:Ch_reply) 387 endif 388 389 " Check that handler works if a zero id message is sent back. 390 let g:Ch_reply = '' 391 let g:Ch_zero_reply = '' 392 call ch_sendexpr(handle, 'send zero', {'callback': 'Ch_oneHandler'}) 393 call WaitForAssert({-> assert_equal('sent zero', g:Ch_zero_reply)}) 394 if s:has_handler 395 call assert_equal('zero index', g:Ch_reply) 396 else 397 call assert_equal('', g:Ch_reply) 398 endif 399endfunc 400 401func Test_zero_reply() 402 " Run with channel handler 403 let s:has_handler = 1 404 let s:chopt.callback = 'Ch_zeroHandler' 405 call s:run_server('Ch_channel_zero') 406 unlet s:chopt.callback 407 408 " Run without channel handler 409 let s:has_handler = 0 410 call s:run_server('Ch_channel_zero') 411endfunc 412 413func Test_zero_reply_ipv6() 414 CheckIPv6 415 call Test_zero_reply() 416endfunc 417 418""""""""" 419 420let g:Ch_reply1 = "" 421func Ch_handleRaw1(chan, msg) 422 unlet g:Ch_reply1 423 let g:Ch_reply1 = a:msg 424endfunc 425 426let g:Ch_reply2 = "" 427func Ch_handleRaw2(chan, msg) 428 unlet g:Ch_reply2 429 let g:Ch_reply2 = a:msg 430endfunc 431 432let g:Ch_reply3 = "" 433func Ch_handleRaw3(chan, msg) 434 unlet g:Ch_reply3 435 let g:Ch_reply3 = a:msg 436endfunc 437 438func Ch_raw_one_time_callback(port) 439 let handle = ch_open(s:localhost . a:port, s:chopt) 440 if ch_status(handle) == "fail" 441 call assert_report("Can't open channel") 442 return 443 endif 444 call ch_setoptions(handle, {'mode': 'raw'}) 445 446 " The messages are sent raw, we do our own JSON strings here. 447 call ch_sendraw(handle, "[1, \"hello!\"]\n", {'callback': 'Ch_handleRaw1'}) 448 call WaitForAssert({-> assert_equal("[1, \"got it\"]", g:Ch_reply1)}) 449 call ch_sendraw(handle, "[2, \"echo something\"]\n", {'callback': 'Ch_handleRaw2'}) 450 call ch_sendraw(handle, "[3, \"wait a bit\"]\n", {'callback': 'Ch_handleRaw3'}) 451 call WaitForAssert({-> assert_equal("[2, \"something\"]", g:Ch_reply2)}) 452 " wait for the 200 msec delayed reply 453 call WaitForAssert({-> assert_equal("[3, \"waited\"]", g:Ch_reply3)}) 454endfunc 455 456func Test_raw_one_time_callback() 457 call s:run_server('Ch_raw_one_time_callback') 458endfunc 459 460func Test_raw_one_time_callback_ipv6() 461 CheckIPv6 462 call Test_raw_one_time_callback() 463endfunc 464 465""""""""" 466 467" Test that trying to connect to a non-existing port fails quickly. 468func Test_connect_waittime() 469 CheckFunction reltimefloat 470 " this is timing sensitive 471 472 let start = reltime() 473 let handle = ch_open('localhost:9876', s:chopt) 474 if ch_status(handle) != "fail" 475 " Oops, port does exists. 476 call ch_close(handle) 477 else 478 let elapsed = reltime(start) 479 call assert_true(reltimefloat(elapsed) < 1.0) 480 endif 481 482 " We intend to use a socket that doesn't exist and wait for half a second 483 " before giving up. If the socket does exist it can fail in various ways. 484 " Check for "Connection reset by peer" to avoid flakiness. 485 let start = reltime() 486 try 487 let handle = ch_open('localhost:9867', {'waittime': 500}) 488 if ch_status(handle) != "fail" 489 " Oops, port does exists. 490 call ch_close(handle) 491 else 492 " Failed connection should wait about 500 msec. Can be longer if the 493 " computer is busy with other things. 494 call assert_inrange(0.3, 1.5, reltimefloat(reltime(start))) 495 endif 496 catch 497 if v:exception !~ 'Connection reset by peer' 498 call assert_report("Caught exception: " . v:exception) 499 endif 500 endtry 501endfunc 502 503""""""""" 504 505func Test_raw_pipe() 506 " Add a dummy close callback to avoid that messages are dropped when calling 507 " ch_canread(). 508 " Also test the non-blocking option. 509 let job = job_start(s:python . " test_channel_pipe.py", 510 \ {'mode': 'raw', 'drop': 'never', 'noblock': 1}) 511 call assert_equal(v:t_job, type(job)) 512 call assert_equal("run", job_status(job)) 513 514 call assert_equal("open", ch_status(job)) 515 call assert_equal("open", ch_status(job), {"part": "out"}) 516 call assert_equal("open", ch_status(job), {"part": "err"}) 517 call assert_fails('call ch_status(job, {"in_mode": "raw"})', 'E475:') 518 call assert_fails('call ch_status(job, {"part": "in"})', 'E475:') 519 520 let dict = ch_info(job) 521 call assert_true(dict.id != 0) 522 call assert_equal('open', dict.status) 523 call assert_equal('open', dict.out_status) 524 call assert_equal('RAW', dict.out_mode) 525 call assert_equal('pipe', dict.out_io) 526 call assert_equal('open', dict.err_status) 527 call assert_equal('RAW', dict.err_mode) 528 call assert_equal('pipe', dict.err_io) 529 530 try 531 " For a change use the job where a channel is expected. 532 call ch_sendraw(job, "echo something\n") 533 let msg = ch_readraw(job) 534 call assert_equal("something\n", substitute(msg, "\r", "", 'g')) 535 536 call ch_sendraw(job, "double this\n") 537 let g:handle = job->job_getchannel() 538 call WaitFor('g:handle->ch_canread()') 539 unlet g:handle 540 let msg = ch_readraw(job) 541 call assert_equal("this\nAND this\n", substitute(msg, "\r", "", 'g')) 542 543 let g:Ch_reply = "" 544 call ch_sendraw(job, "double this\n", {'callback': 'Ch_handler'}) 545 call WaitForAssert({-> assert_equal("this\nAND this\n", substitute(g:Ch_reply, "\r", "", 'g'))}) 546 547 call assert_fails("let i = ch_evalraw(job, '2 + 2', {'callback' : 'abc'})", 'E917:') 548 call assert_fails("let i = ch_evalexpr(job, '2 + 2')", 'E912:') 549 call assert_fails("let i = ch_evalraw(job, '2 + 2', {'drop' : ''})", 'E475:') 550 call assert_fails("let i = ch_evalraw(test_null_job(), '2 + 2')", 'E906:') 551 552 let reply = job->ch_evalraw("quit\n", {'timeout': 100}) 553 call assert_equal("Goodbye!\n", substitute(reply, "\r", "", 'g')) 554 finally 555 call job_stop(job) 556 endtry 557 558 let g:Ch_job = job 559 call WaitForAssert({-> assert_equal("dead", job_status(g:Ch_job))}) 560 let info = job->job_info() 561 call assert_equal("dead", info.status) 562 call assert_equal("term", info.stoponexit) 563 call assert_equal(2, len(info.cmd)) 564 call assert_equal("test_channel_pipe.py", info.cmd[1]) 565 566 let found = 0 567 for j in job_info() 568 if j == job 569 let found += 1 570 endif 571 endfor 572 call assert_equal(1, found) 573 574 call assert_fails("call job_stop('abc')", 'E475:') 575 call assert_fails("call job_stop(job, [])", 'E730:') 576 call assert_fails("call job_stop(test_null_job())", 'E916:') 577 578 " Try to use the job and channel where a number is expected. This is not 579 " related to testing the raw pipe. This test is here just to reuse the 580 " already created job/channel. 581 let ch = job_getchannel(job) 582 call assert_fails('let i = job + 1', 'E910:') 583 call assert_fails('let j = ch + 1', 'E913:') 584 call assert_fails('echo 2.0 == job', 'E911:') 585 call assert_fails('echo 2.0 == ch', 'E914:') 586endfunc 587 588func Test_raw_pipe_blob() 589 " Add a dummy close callback to avoid that messages are dropped when calling 590 " ch_canread(). 591 " Also test the non-blocking option. 592 let job = job_start(s:python . " test_channel_pipe.py", 593 \ {'mode': 'raw', 'drop': 'never', 'noblock': 1}) 594 call assert_equal(v:t_job, type(job)) 595 call assert_equal("run", job_status(job)) 596 597 call assert_equal("open", ch_status(job)) 598 call assert_equal("open", ch_status(job), {"part": "out"}) 599 600 try 601 " Create a blob with the echo command and write it. 602 let blob = 0z00 603 let cmd = "echo something\n" 604 for i in range(0, len(cmd) - 1) 605 let blob[i] = char2nr(cmd[i]) 606 endfor 607 call assert_equal(len(cmd), len(blob)) 608 call ch_sendraw(job, blob) 609 610 " Read a blob with the reply. 611 let msg = job->ch_readblob() 612 let expected = 'something' 613 for i in range(0, len(expected) - 1) 614 call assert_equal(char2nr(expected[i]), msg[i]) 615 endfor 616 617 let reply = ch_evalraw(job, "quit\n", {'timeout': 100}) 618 call assert_equal("Goodbye!\n", substitute(reply, "\r", "", 'g')) 619 finally 620 call job_stop(job) 621 endtry 622 623 let g:Ch_job = job 624 call WaitForAssert({-> assert_equal("dead", job_status(g:Ch_job))}) 625 let info = job_info(job) 626 call assert_equal("dead", info.status) 627endfunc 628 629func Test_nl_pipe() 630 let job = job_start([s:python, "test_channel_pipe.py"]) 631 call assert_equal("run", job_status(job)) 632 try 633 let handle = job_getchannel(job) 634 call ch_sendraw(handle, "echo something\n") 635 call assert_equal("something", handle->ch_readraw()) 636 637 call ch_sendraw(handle, "echoerr wrong\n") 638 call assert_equal("wrong", ch_readraw(handle, {'part': 'err'})) 639 640 call ch_sendraw(handle, "double this\n") 641 call assert_equal("this", ch_readraw(handle)) 642 call assert_equal("AND this", ch_readraw(handle)) 643 644 call ch_sendraw(handle, "split this line\n") 645 call assert_equal("this linethis linethis line", handle->ch_read()) 646 647 let reply = ch_evalraw(handle, "quit\n") 648 call assert_equal("Goodbye!", reply) 649 finally 650 call job_stop(job) 651 endtry 652endfunc 653 654func Stop_g_job() 655 call job_stop(g:job) 656 if has('win32') 657 " On MS-Windows the server must close the file handle before we are able 658 " to delete the file. 659 call WaitForAssert({-> assert_equal('dead', job_status(g:job))}) 660 sleep 10m 661 endif 662endfunc 663 664func Test_nl_read_file() 665 call writefile(['echo something', 'echoerr wrong', 'double this'], 'Xinput') 666 let g:job = job_start(s:python . " test_channel_pipe.py", 667 \ {'in_io': 'file', 'in_name': 'Xinput'}) 668 call assert_equal("run", job_status(g:job)) 669 try 670 let handle = job_getchannel(g:job) 671 call assert_equal("something", ch_readraw(handle)) 672 call assert_equal("wrong", ch_readraw(handle, {'part': 'err'})) 673 call assert_equal("this", ch_readraw(handle)) 674 call assert_equal("AND this", ch_readraw(handle)) 675 finally 676 call Stop_g_job() 677 call delete('Xinput') 678 endtry 679 call assert_fails("echo ch_read(test_null_channel(), {'callback' : 'abc'})", 'E475:') 680endfunc 681 682func Test_nl_write_out_file() 683 let g:job = job_start(s:python . " test_channel_pipe.py", 684 \ {'out_io': 'file', 'out_name': 'Xoutput'}) 685 call assert_equal("run", job_status(g:job)) 686 try 687 let handle = job_getchannel(g:job) 688 call ch_sendraw(handle, "echo line one\n") 689 call ch_sendraw(handle, "echo line two\n") 690 call ch_sendraw(handle, "double this\n") 691 call WaitForAssert({-> assert_equal(['line one', 'line two', 'this', 'AND this'], readfile('Xoutput'))}) 692 finally 693 call Stop_g_job() 694 call assert_equal(-1, match(s:get_resources(), '\(^\|/\)Xoutput$')) 695 call delete('Xoutput') 696 endtry 697endfunc 698 699func Test_nl_write_err_file() 700 let g:job = job_start(s:python . " test_channel_pipe.py", 701 \ {'err_io': 'file', 'err_name': 'Xoutput'}) 702 call assert_equal("run", job_status(g:job)) 703 try 704 let handle = job_getchannel(g:job) 705 call ch_sendraw(handle, "echoerr line one\n") 706 call ch_sendraw(handle, "echoerr line two\n") 707 call ch_sendraw(handle, "doubleerr this\n") 708 call WaitForAssert({-> assert_equal(['line one', 'line two', 'this', 'AND this'], readfile('Xoutput'))}) 709 finally 710 call Stop_g_job() 711 call delete('Xoutput') 712 endtry 713endfunc 714 715func Test_nl_write_both_file() 716 let g:job = job_start(s:python . " test_channel_pipe.py", 717 \ {'out_io': 'file', 'out_name': 'Xoutput', 'err_io': 'out'}) 718 call assert_equal("run", job_status(g:job)) 719 try 720 let handle = job_getchannel(g:job) 721 call ch_sendraw(handle, "echoerr line one\n") 722 call ch_sendraw(handle, "echo line two\n") 723 call ch_sendraw(handle, "double this\n") 724 call ch_sendraw(handle, "doubleerr that\n") 725 call WaitForAssert({-> assert_equal(['line one', 'line two', 'this', 'AND this', 'that', 'AND that'], readfile('Xoutput'))}) 726 finally 727 call Stop_g_job() 728 call assert_equal(-1, match(s:get_resources(), '\(^\|/\)Xoutput$')) 729 call delete('Xoutput') 730 endtry 731endfunc 732 733func BufCloseCb(ch) 734 let g:Ch_bufClosed = 'yes' 735endfunc 736 737func Run_test_pipe_to_buffer(use_name, nomod, do_msg) 738 let g:Ch_bufClosed = 'no' 739 let options = {'out_io': 'buffer', 'close_cb': 'BufCloseCb'} 740 let expected = ['', 'line one', 'line two', 'this', 'AND this', 'Goodbye!'] 741 if a:use_name 742 let options['out_name'] = 'pipe-output' 743 if a:do_msg 744 let expected[0] = 'Reading from channel output...' 745 else 746 let options['out_msg'] = 0 747 call remove(expected, 0) 748 endif 749 else 750 sp pipe-output 751 let options['out_buf'] = bufnr('%') 752 quit 753 call remove(expected, 0) 754 endif 755 if a:nomod 756 let options['out_modifiable'] = 0 757 endif 758 let job = job_start(s:python . " test_channel_pipe.py", options) 759 call assert_equal("run", job_status(job)) 760 try 761 let handle = job_getchannel(job) 762 call ch_sendraw(handle, "echo line one\n") 763 call ch_sendraw(handle, "echo line two\n") 764 call ch_sendraw(handle, "double this\n") 765 call ch_sendraw(handle, "quit\n") 766 sp pipe-output 767 call WaitFor('line("$") == ' . len(expected) . ' && g:Ch_bufClosed == "yes"') 768 call assert_equal(expected, getline(1, '$')) 769 if a:nomod 770 call assert_equal(0, &modifiable) 771 else 772 call assert_equal(1, &modifiable) 773 endif 774 call assert_equal('yes', g:Ch_bufClosed) 775 bwipe! 776 finally 777 call job_stop(job) 778 endtry 779endfunc 780 781func Test_pipe_to_buffer_name() 782 call Run_test_pipe_to_buffer(1, 0, 1) 783endfunc 784 785func Test_pipe_to_buffer_nr() 786 call Run_test_pipe_to_buffer(0, 0, 1) 787endfunc 788 789func Test_pipe_to_buffer_name_nomod() 790 call Run_test_pipe_to_buffer(1, 1, 1) 791endfunc 792 793func Test_pipe_to_buffer_name_nomsg() 794 call Run_test_pipe_to_buffer(1, 0, 1) 795endfunc 796 797func Test_close_output_buffer() 798 enew! 799 let test_lines = ['one', 'two'] 800 call setline(1, test_lines) 801 let options = {'out_io': 'buffer'} 802 let options['out_name'] = 'buffer-output' 803 let options['out_msg'] = 0 804 split buffer-output 805 let job = job_start(s:python . " test_channel_write.py", options) 806 call assert_equal("run", job_status(job)) 807 try 808 call WaitForAssert({-> assert_equal(3, line('$'))}) 809 quit! 810 sleep 100m 811 " Make sure the write didn't happen to the wrong buffer. 812 call assert_equal(test_lines, getline(1, line('$'))) 813 call assert_equal(-1, bufwinnr('buffer-output')) 814 sbuf buffer-output 815 call assert_notequal(-1, bufwinnr('buffer-output')) 816 sleep 100m 817 close " no more writes 818 bwipe! 819 finally 820 call job_stop(job) 821 endtry 822endfunc 823 824func Run_test_pipe_err_to_buffer(use_name, nomod, do_msg) 825 let options = {'err_io': 'buffer'} 826 let expected = ['', 'line one', 'line two', 'this', 'AND this'] 827 if a:use_name 828 let options['err_name'] = 'pipe-err' 829 if a:do_msg 830 let expected[0] = 'Reading from channel error...' 831 else 832 let options['err_msg'] = 0 833 call remove(expected, 0) 834 endif 835 else 836 sp pipe-err 837 let options['err_buf'] = bufnr('%') 838 quit 839 call remove(expected, 0) 840 endif 841 if a:nomod 842 let options['err_modifiable'] = 0 843 endif 844 let job = job_start(s:python . " test_channel_pipe.py", options) 845 call assert_equal("run", job_status(job)) 846 try 847 let handle = job_getchannel(job) 848 call ch_sendraw(handle, "echoerr line one\n") 849 call ch_sendraw(handle, "echoerr line two\n") 850 call ch_sendraw(handle, "doubleerr this\n") 851 call ch_sendraw(handle, "quit\n") 852 sp pipe-err 853 call WaitForAssert({-> assert_equal(expected, getline(1, '$'))}) 854 if a:nomod 855 call assert_equal(0, &modifiable) 856 else 857 call assert_equal(1, &modifiable) 858 endif 859 bwipe! 860 finally 861 call job_stop(job) 862 endtry 863endfunc 864 865func Test_pipe_err_to_buffer_name() 866 call Run_test_pipe_err_to_buffer(1, 0, 1) 867endfunc 868 869func Test_pipe_err_to_buffer_nr() 870 call Run_test_pipe_err_to_buffer(0, 0, 1) 871endfunc 872 873func Test_pipe_err_to_buffer_name_nomod() 874 call Run_test_pipe_err_to_buffer(1, 1, 1) 875endfunc 876 877func Test_pipe_err_to_buffer_name_nomsg() 878 call Run_test_pipe_err_to_buffer(1, 0, 0) 879endfunc 880 881func Test_pipe_both_to_buffer() 882 let job = job_start(s:python . " test_channel_pipe.py", 883 \ {'out_io': 'buffer', 'out_name': 'pipe-err', 'err_io': 'out'}) 884 call assert_equal("run", job_status(job)) 885 let handle = job_getchannel(job) 886 call assert_equal(bufnr('pipe-err'), ch_getbufnr(handle, 'out')) 887 call assert_equal(bufnr('pipe-err'), ch_getbufnr(handle, 'err')) 888 try 889 call ch_sendraw(handle, "echo line one\n") 890 call ch_sendraw(handle, "echoerr line two\n") 891 call ch_sendraw(handle, "double this\n") 892 call ch_sendraw(handle, "doubleerr that\n") 893 call ch_sendraw(handle, "quit\n") 894 sp pipe-err 895 call WaitForAssert({-> assert_equal(['Reading from channel output...', 'line one', 'line two', 'this', 'AND this', 'that', 'AND that', 'Goodbye!'], getline(1, '$'))}) 896 bwipe! 897 finally 898 call job_stop(job) 899 endtry 900endfunc 901 902func Run_test_pipe_from_buffer(use_name) 903 sp pipe-input 904 call setline(1, ['echo one', 'echo two', 'echo three']) 905 let options = {'in_io': 'buffer', 'block_write': 1} 906 if a:use_name 907 let options['in_name'] = 'pipe-input' 908 else 909 let options['in_buf'] = bufnr('%') 910 endif 911 912 let job = job_start(s:python . " test_channel_pipe.py", options) 913 call assert_equal("run", job_status(job)) 914 if has('unix') && !a:use_name 915 call assert_equal(bufnr('%'), ch_getbufnr(job, 'in')) 916 endif 917 try 918 let handle = job_getchannel(job) 919 call assert_equal('one', ch_read(handle)) 920 call assert_equal('two', ch_read(handle)) 921 call assert_equal('three', ch_read(handle)) 922 bwipe! 923 finally 924 call job_stop(job) 925 endtry 926endfunc 927 928func Test_pipe_from_buffer_name() 929 call Run_test_pipe_from_buffer(1) 930endfunc 931 932func Test_pipe_from_buffer_nr() 933 call Run_test_pipe_from_buffer(0) 934endfunc 935 936func Run_pipe_through_sort(all, use_buffer) 937 CheckExecutable sort 938 939 let options = {'out_io': 'buffer', 'out_name': 'sortout'} 940 if a:use_buffer 941 split sortin 942 call setline(1, ['ccc', 'aaa', 'ddd', 'bbb', 'eee']) 943 let options.in_io = 'buffer' 944 let options.in_name = 'sortin' 945 endif 946 if !a:all 947 let options.in_top = 2 948 let options.in_bot = 4 949 endif 950 let job = job_start('sort', options) 951 952 if !a:use_buffer 953 call assert_equal("run", job_status(job)) 954 call ch_sendraw(job, "ccc\naaa\nddd\nbbb\neee\n") 955 eval job->ch_close_in() 956 endif 957 958 call WaitForAssert({-> assert_equal("dead", job_status(job))}) 959 960 sp sortout 961 call WaitFor('line("$") > 3') 962 call assert_equal('Reading from channel output...', getline(1)) 963 if a:all 964 call assert_equal(['aaa', 'bbb', 'ccc', 'ddd', 'eee'], getline(2, 6)) 965 else 966 call assert_equal(['aaa', 'bbb', 'ddd'], getline(2, 4)) 967 endif 968 969 call job_stop(job) 970 if a:use_buffer 971 bwipe! sortin 972 endif 973 bwipe! sortout 974endfunc 975 976func Test_pipe_through_sort_all() 977 call Run_pipe_through_sort(1, 1) 978endfunc 979 980func Test_pipe_through_sort_some() 981 call Run_pipe_through_sort(0, 1) 982endfunc 983 984func Test_pipe_through_sort_feed() 985 call Run_pipe_through_sort(1, 0) 986endfunc 987 988func Test_pipe_to_nameless_buffer() 989 let job = job_start(s:python . " test_channel_pipe.py", 990 \ {'out_io': 'buffer'}) 991 call assert_equal("run", job_status(job)) 992 try 993 let handle = job_getchannel(job) 994 call ch_sendraw(handle, "echo line one\n") 995 call ch_sendraw(handle, "echo line two\n") 996 exe handle->ch_getbufnr("out") .. 'sbuf' 997 call WaitFor('line("$") >= 3') 998 call assert_equal(['Reading from channel output...', 'line one', 'line two'], getline(1, '$')) 999 bwipe! 1000 finally 1001 call job_stop(job) 1002 endtry 1003endfunc 1004 1005func Test_pipe_to_buffer_json() 1006 CheckFunction reltimefloat 1007 1008 let job = job_start(s:python . " test_channel_pipe.py", 1009 \ {'out_io': 'buffer', 'out_mode': 'json'}) 1010 call assert_equal("run", job_status(job)) 1011 try 1012 let handle = job_getchannel(job) 1013 call ch_sendraw(handle, "echo [0, \"hello\"]\n") 1014 call ch_sendraw(handle, "echo [-2, 12.34]\n") 1015 exe ch_getbufnr(handle, "out") . 'sbuf' 1016 call WaitFor('line("$") >= 3') 1017 call assert_equal(['Reading from channel output...', '[0,"hello"]', '[-2,12.34]'], getline(1, '$')) 1018 bwipe! 1019 finally 1020 call job_stop(job) 1021 endtry 1022endfunc 1023 1024" Wait a little while for the last line, minus "offset", to equal "line". 1025func s:wait_for_last_line(line, offset) 1026 for i in range(100) 1027 if getline(line('$') - a:offset) == a:line 1028 break 1029 endif 1030 sleep 10m 1031 endfor 1032endfunc 1033 1034func Test_pipe_io_two_buffers() 1035 " Create two buffers, one to read from and one to write to. 1036 split pipe-output 1037 set buftype=nofile 1038 split pipe-input 1039 set buftype=nofile 1040 1041 let job = job_start(s:python . " test_channel_pipe.py", 1042 \ {'in_io': 'buffer', 'in_name': 'pipe-input', 'in_top': 0, 1043 \ 'out_io': 'buffer', 'out_name': 'pipe-output', 1044 \ 'block_write': 1}) 1045 call assert_equal("run", job_status(job)) 1046 try 1047 exe "normal Gaecho hello\<CR>" 1048 exe bufwinnr('pipe-output') . "wincmd w" 1049 call s:wait_for_last_line('hello', 0) 1050 call assert_equal('hello', getline('$')) 1051 1052 exe bufwinnr('pipe-input') . "wincmd w" 1053 exe "normal Gadouble this\<CR>" 1054 exe bufwinnr('pipe-output') . "wincmd w" 1055 call s:wait_for_last_line('AND this', 0) 1056 call assert_equal('this', getline(line('$') - 1)) 1057 call assert_equal('AND this', getline('$')) 1058 1059 bwipe! 1060 exe bufwinnr('pipe-input') . "wincmd w" 1061 bwipe! 1062 finally 1063 call job_stop(job) 1064 endtry 1065endfunc 1066 1067func Test_pipe_io_one_buffer() 1068 " Create one buffer to read from and to write to. 1069 split pipe-io 1070 set buftype=nofile 1071 1072 let job = job_start(s:python . " test_channel_pipe.py", 1073 \ {'in_io': 'buffer', 'in_name': 'pipe-io', 'in_top': 0, 1074 \ 'out_io': 'buffer', 'out_name': 'pipe-io', 1075 \ 'block_write': 1}) 1076 call assert_equal("run", job_status(job)) 1077 try 1078 exe "normal Goecho hello\<CR>" 1079 call s:wait_for_last_line('hello', 1) 1080 call assert_equal('hello', getline(line('$') - 1)) 1081 1082 exe "normal Gadouble this\<CR>" 1083 call s:wait_for_last_line('AND this', 1) 1084 call assert_equal('this', getline(line('$') - 2)) 1085 call assert_equal('AND this', getline(line('$') - 1)) 1086 1087 bwipe! 1088 finally 1089 call job_stop(job) 1090 endtry 1091endfunc 1092 1093func Test_write_to_buffer_and_scroll() 1094 CheckScreendump 1095 1096 let lines =<< trim END 1097 new Xscrollbuffer 1098 call setline(1, range(1, 200)) 1099 $ 1100 redraw 1101 wincmd w 1102 call deletebufline('Xscrollbuffer', 1, '$') 1103 if has('win32') 1104 let cmd = ['cmd', '/c', 'echo sometext'] 1105 else 1106 let cmd = [&shell, &shellcmdflag, 'echo sometext'] 1107 endif 1108 call job_start(cmd, #{out_io: 'buffer', out_name: 'Xscrollbuffer'}) 1109 END 1110 call writefile(lines, 'XtestBufferScroll') 1111 let buf = RunVimInTerminal('-S XtestBufferScroll', #{rows: 10}) 1112 call TermWait(buf, 50) 1113 call VerifyScreenDump(buf, 'Test_job_buffer_scroll_1', {}) 1114 1115 " clean up 1116 call StopVimInTerminal(buf) 1117 call delete('XtestBufferScroll') 1118endfunc 1119 1120func Test_pipe_null() 1121 " We cannot check that no I/O works, we only check that the job starts 1122 " properly. 1123 let job = job_start(s:python . " test_channel_pipe.py something", 1124 \ {'in_io': 'null'}) 1125 call assert_equal("run", job_status(job)) 1126 try 1127 call assert_equal('something', ch_read(job)) 1128 finally 1129 call job_stop(job) 1130 endtry 1131 1132 let job = job_start(s:python . " test_channel_pipe.py err-out", 1133 \ {'out_io': 'null'}) 1134 call assert_equal("run", job_status(job)) 1135 try 1136 call assert_equal('err-out', ch_read(job, {"part": "err"})) 1137 finally 1138 call job_stop(job) 1139 endtry 1140 1141 let job = job_start(s:python . " test_channel_pipe.py something", 1142 \ {'err_io': 'null'}) 1143 call assert_equal("run", job_status(job)) 1144 try 1145 call assert_equal('something', ch_read(job)) 1146 finally 1147 call job_stop(job) 1148 endtry 1149 1150 let job = job_start(s:python . " test_channel_pipe.py something", 1151 \ {'out_io': 'null', 'err_io': 'out'}) 1152 call assert_equal("run", job_status(job)) 1153 call job_stop(job) 1154 1155 let job = job_start(s:python . " test_channel_pipe.py something", 1156 \ {'in_io': 'null', 'out_io': 'null', 'err_io': 'null'}) 1157 call assert_equal("run", job_status(job)) 1158 call assert_equal('channel fail', string(job_getchannel(job))) 1159 call assert_equal('fail', ch_status(job)) 1160 call assert_equal('no process', string(test_null_job())) 1161 call assert_equal('channel fail', string(test_null_channel())) 1162 call job_stop(job) 1163endfunc 1164 1165func Test_pipe_to_buffer_raw() 1166 let options = {'out_mode': 'raw', 'out_io': 'buffer', 'out_name': 'testout'} 1167 split testout 1168 let job = job_start([s:python, '-c', 1169 \ 'import sys; [sys.stdout.write(".") and sys.stdout.flush() for _ in range(10000)]'], options) 1170 " the job may be done quickly, also accept "dead" 1171 call assert_match('^\%(dead\|run\)$', job_status(job)) 1172 call WaitFor('len(join(getline(1, "$"), "")) >= 10000') 1173 try 1174 let totlen = 0 1175 for line in getline(1, '$') 1176 call assert_equal('', substitute(line, '^\.*', '', '')) 1177 let totlen += len(line) 1178 endfor 1179 call assert_equal(10000, totlen) 1180 finally 1181 call job_stop(job) 1182 bwipe! 1183 endtry 1184endfunc 1185 1186func Test_reuse_channel() 1187 let job = job_start(s:python . " test_channel_pipe.py") 1188 call assert_equal("run", job_status(job)) 1189 let handle = job_getchannel(job) 1190 try 1191 call ch_sendraw(handle, "echo something\n") 1192 call assert_equal("something", ch_readraw(handle)) 1193 finally 1194 call job_stop(job) 1195 endtry 1196 1197 let job = job_start(s:python . " test_channel_pipe.py", {'channel': handle}) 1198 call assert_equal("run", job_status(job)) 1199 let handle = job_getchannel(job) 1200 try 1201 call ch_sendraw(handle, "echo again\n") 1202 call assert_equal("again", ch_readraw(handle)) 1203 finally 1204 call job_stop(job) 1205 endtry 1206endfunc 1207 1208func Test_out_cb() 1209 let dict = {'thisis': 'dict: '} 1210 func dict.outHandler(chan, msg) dict 1211 if type(a:msg) == v:t_string 1212 let g:Ch_outmsg = self.thisis . a:msg 1213 else 1214 let g:Ch_outobj = a:msg 1215 endif 1216 endfunc 1217 func dict.errHandler(chan, msg) dict 1218 let g:Ch_errmsg = self.thisis . a:msg 1219 endfunc 1220 let job = job_start(s:python . " test_channel_pipe.py", 1221 \ {'out_cb': dict.outHandler, 1222 \ 'out_mode': 'json', 1223 \ 'err_cb': dict.errHandler, 1224 \ 'err_mode': 'json'}) 1225 call assert_equal("run", job_status(job)) 1226 call test_garbagecollect_now() 1227 try 1228 let g:Ch_outmsg = '' 1229 let g:Ch_errmsg = '' 1230 call ch_sendraw(job, "echo [0, \"hello\"]\n") 1231 call ch_sendraw(job, "echoerr [0, \"there\"]\n") 1232 call WaitForAssert({-> assert_equal("dict: hello", g:Ch_outmsg)}) 1233 call WaitForAssert({-> assert_equal("dict: there", g:Ch_errmsg)}) 1234 1235 " Receive a json object split in pieces 1236 let g:Ch_outobj = '' 1237 call ch_sendraw(job, "echosplit [0, {\"one\": 1,| \"tw|o\": 2, \"three\": 3|}]\n") 1238 " For unknown reasons this can be very slow on Mac. 1239 if has('mac') 1240 let timeout = 20000 1241 else 1242 let timeout = 5000 1243 endif 1244 call WaitForAssert({-> assert_equal({'one': 1, 'two': 2, 'three': 3}, g:Ch_outobj)}, timeout) 1245 finally 1246 call job_stop(job) 1247 endtry 1248endfunc 1249 1250func Test_out_close_cb() 1251 let s:counter = 1 1252 let g:Ch_msg1 = '' 1253 let g:Ch_closemsg = 0 1254 func! OutHandler(chan, msg) 1255 if s:counter == 1 1256 let g:Ch_msg1 = a:msg 1257 endif 1258 let s:counter += 1 1259 endfunc 1260 func! CloseHandler(chan) 1261 let g:Ch_closemsg = s:counter 1262 let s:counter += 1 1263 endfunc 1264 let job = job_start(s:python . " test_channel_pipe.py quit now", 1265 \ {'out_cb': 'OutHandler', 1266 \ 'close_cb': 'CloseHandler'}) 1267 " the job may be done quickly, also accept "dead" 1268 call assert_match('^\%(dead\|run\)$', job_status(job)) 1269 try 1270 call WaitForAssert({-> assert_equal('quit', g:Ch_msg1)}) 1271 call WaitForAssert({-> assert_equal(2, g:Ch_closemsg)}) 1272 finally 1273 call job_stop(job) 1274 delfunc OutHandler 1275 delfunc CloseHandler 1276 endtry 1277endfunc 1278 1279func Test_read_in_close_cb() 1280 let g:Ch_received = '' 1281 func! CloseHandler(chan) 1282 let g:Ch_received = ch_read(a:chan) 1283 endfunc 1284 let job = job_start(s:python . " test_channel_pipe.py quit now", 1285 \ {'close_cb': 'CloseHandler'}) 1286 " the job may be done quickly, also accept "dead" 1287 call assert_match('^\%(dead\|run\)$', job_status(job)) 1288 try 1289 call WaitForAssert({-> assert_equal('quit', g:Ch_received)}) 1290 finally 1291 call job_stop(job) 1292 delfunc CloseHandler 1293 endtry 1294endfunc 1295 1296" Use channel in NL mode but received text does not end in NL. 1297func Test_read_in_close_cb_incomplete() 1298 let g:Ch_received = '' 1299 func! CloseHandler(chan) 1300 while ch_status(a:chan, {'part': 'out'}) == 'buffered' 1301 let g:Ch_received .= ch_read(a:chan) 1302 endwhile 1303 endfunc 1304 let job = job_start(s:python . " test_channel_pipe.py incomplete", 1305 \ {'close_cb': 'CloseHandler'}) 1306 " the job may be done quickly, also accept "dead" 1307 call assert_match('^\%(dead\|run\)$', job_status(job)) 1308 try 1309 call WaitForAssert({-> assert_equal('incomplete', g:Ch_received)}) 1310 finally 1311 call job_stop(job) 1312 delfunc CloseHandler 1313 endtry 1314endfunc 1315 1316func Test_out_cb_lambda() 1317 let job = job_start(s:python . " test_channel_pipe.py", 1318 \ {'out_cb': {ch, msg -> execute("let g:Ch_outmsg = 'lambda: ' . msg")}, 1319 \ 'out_mode': 'json', 1320 \ 'err_cb': {ch, msg -> execute(":let g:Ch_errmsg = 'lambda: ' . msg")}, 1321 \ 'err_mode': 'json'}) 1322 call assert_equal("run", job_status(job)) 1323 try 1324 let g:Ch_outmsg = '' 1325 let g:Ch_errmsg = '' 1326 call ch_sendraw(job, "echo [0, \"hello\"]\n") 1327 call ch_sendraw(job, "echoerr [0, \"there\"]\n") 1328 call WaitForAssert({-> assert_equal("lambda: hello", g:Ch_outmsg)}) 1329 call WaitForAssert({-> assert_equal("lambda: there", g:Ch_errmsg)}) 1330 finally 1331 call job_stop(job) 1332 endtry 1333endfunc 1334 1335func Test_close_and_exit_cb() 1336 let g:retdict = {'ret': {}} 1337 func g:retdict.close_cb(ch) dict 1338 let self.ret['close_cb'] = a:ch->ch_getjob()->job_status() 1339 endfunc 1340 func g:retdict.exit_cb(job, status) dict 1341 let self.ret['exit_cb'] = job_status(a:job) 1342 endfunc 1343 1344 let job = job_start([&shell, &shellcmdflag, 'echo'], 1345 \ {'close_cb': g:retdict.close_cb, 1346 \ 'exit_cb': g:retdict.exit_cb}) 1347 " the job may be done quickly, also accept "dead" 1348 call assert_match('^\%(dead\|run\)$', job_status(job)) 1349 call WaitForAssert({-> assert_equal(2, len(g:retdict.ret))}) 1350 call assert_match('^\%(dead\|run\)$', g:retdict.ret['close_cb']) 1351 call assert_equal('dead', g:retdict.ret['exit_cb']) 1352 unlet g:retdict 1353endfunc 1354 1355"""""""""" 1356 1357function ExitCbWipe(job, status) 1358 exe g:wipe_buf 'bw!' 1359endfunction 1360 1361" This caused a crash, because messages were handled while peeking for a 1362" character. 1363func Test_exit_cb_wipes_buf() 1364 CheckFeature timers 1365 set cursorline lazyredraw 1366 call test_override('redraw_flag', 1) 1367 new 1368 let g:wipe_buf = bufnr('') 1369 1370 let job = job_start(has('win32') ? 'cmd /c echo:' : ['true'], 1371 \ {'exit_cb': 'ExitCbWipe'}) 1372 let timer = timer_start(300, {-> feedkeys("\<Esc>", 'nt')}, {'repeat': 5}) 1373 call feedkeys(repeat('g', 1000) . 'o', 'ntx!') 1374 call WaitForAssert({-> assert_equal("dead", job_status(job))}) 1375 call timer_stop(timer) 1376 1377 set nocursorline nolazyredraw 1378 unlet g:wipe_buf 1379 call test_override('ALL', 0) 1380endfunc 1381 1382"""""""""" 1383 1384let g:Ch_unletResponse = '' 1385func s:UnletHandler(handle, msg) 1386 let g:Ch_unletResponse = a:msg 1387 unlet s:channelfd 1388endfunc 1389 1390" Test that "unlet handle" in a handler doesn't crash Vim. 1391func Ch_unlet_handle(port) 1392 let s:channelfd = ch_open(s:localhost . a:port, s:chopt) 1393 eval s:channelfd->ch_sendexpr("test", {'callback': function('s:UnletHandler')}) 1394 call WaitForAssert({-> assert_equal('what?', g:Ch_unletResponse)}) 1395endfunc 1396 1397func Test_unlet_handle() 1398 call s:run_server('Ch_unlet_handle') 1399endfunc 1400 1401func Test_unlet_handle_ipv6() 1402 CheckIPv6 1403 call Test_unlet_handle() 1404endfunc 1405 1406"""""""""" 1407 1408let g:Ch_unletResponse = '' 1409func Ch_CloseHandler(handle, msg) 1410 let g:Ch_unletResponse = a:msg 1411 eval s:channelfd->ch_close() 1412endfunc 1413 1414" Test that "unlet handle" in a handler doesn't crash Vim. 1415func Ch_close_handle(port) 1416 let s:channelfd = ch_open(s:localhost . a:port, s:chopt) 1417 call ch_sendexpr(s:channelfd, "test", {'callback': function('Ch_CloseHandler')}) 1418 call WaitForAssert({-> assert_equal('what?', g:Ch_unletResponse)}) 1419endfunc 1420 1421func Test_close_handle() 1422 call s:run_server('Ch_close_handle') 1423endfunc 1424 1425func Test_close_handle_ipv6() 1426 CheckIPv6 1427 call Test_close_handle() 1428endfunc 1429 1430"""""""""" 1431 1432func Ch_open_ipv6(port) 1433 let handle = ch_open('[::1]:' .. a:port, s:chopt) 1434 call assert_notequal('fail', ch_status(handle)) 1435endfunc 1436 1437func Test_open_ipv6() 1438 CheckIPv6 1439 call s:run_server('Ch_open_ipv6') 1440endfunc 1441 1442"""""""""" 1443 1444func Test_open_fail() 1445 call assert_fails("let ch = ch_open('noserver')", 'E475:') 1446 echo ch 1447 let d = ch 1448 call assert_fails("let ch = ch_open('noserver', 10)", 'E474:') 1449 call assert_fails("let ch = ch_open('localhost:-1')", 'E475:') 1450 call assert_fails("let ch = ch_open('localhost:65537')", 'E475:') 1451 call assert_fails("let ch = ch_open('localhost:8765', {'timeout' : -1})", 1452 \ 'E474:') 1453 call assert_fails("let ch = ch_open('localhost:8765', {'axby' : 1})", 1454 \ 'E475:') 1455 call assert_fails("let ch = ch_open('localhost:8765', {'mode' : 'abc'})", 1456 \ 'E475:') 1457 call assert_fails("let ch = ch_open('localhost:8765', {'part' : 'out'})", 1458 \ 'E475:') 1459 call assert_fails("let ch = ch_open('[::]')", 'E475:') 1460 call assert_fails("let ch = ch_open('[::.80')", 'E475:') 1461 call assert_fails("let ch = ch_open('[::]8080')", 'E475:') 1462endfunc 1463 1464func Test_ch_info_fail() 1465 call assert_fails("let x = ch_info(10)", 'E475:') 1466endfunc 1467 1468"""""""""" 1469 1470func Ch_open_delay(port) 1471 " Wait up to a second for the port to open. 1472 let s:chopt.waittime = 1000 1473 let channel = ch_open(s:localhost . a:port, s:chopt) 1474 if ch_status(channel) == "fail" 1475 call assert_report("Can't open channel") 1476 return 1477 endif 1478 call assert_equal('got it', channel->ch_evalexpr('hello!')) 1479 call ch_close(channel) 1480endfunc 1481 1482func Test_open_delay() 1483 " The server will wait half a second before creating the port. 1484 call s:run_server('Ch_open_delay', 'delay') 1485endfunc 1486 1487func Test_open_delay_ipv6() 1488 CheckIPv6 1489 call Test_open_delay() 1490endfunc 1491 1492""""""""" 1493 1494function MyFunction(a,b,c) 1495 let g:Ch_call_ret = [a:a, a:b, a:c] 1496endfunc 1497 1498function Ch_test_call(port) 1499 let handle = ch_open(s:localhost . a:port, s:chopt) 1500 if ch_status(handle) == "fail" 1501 call assert_report("Can't open channel") 1502 return 1503 endif 1504 1505 let g:Ch_call_ret = [] 1506 call assert_equal('ok', ch_evalexpr(handle, 'call-func')) 1507 call WaitForAssert({-> assert_equal([1, 2, 3], g:Ch_call_ret)}) 1508 1509 call assert_fails("let i = ch_evalexpr(handle, '2 + 2', {'callback' : 'abc'})", 'E917:') 1510 call assert_fails("let i = ch_evalexpr(handle, '2 + 2', {'drop' : ''})", 'E475:') 1511 call assert_fails("let i = ch_evalexpr(test_null_job(), '2 + 2')", 'E906:') 1512endfunc 1513 1514func Test_call() 1515 call s:run_server('Ch_test_call') 1516endfunc 1517 1518func Test_call_ipv6() 1519 CheckIPv6 1520 call Test_call() 1521endfunc 1522 1523""""""""" 1524 1525let g:Ch_job_exit_ret = 'not yet' 1526function MyExitCb(job, status) 1527 let g:Ch_job_exit_ret = 'done' 1528endfunc 1529 1530function Ch_test_exit_callback(port) 1531 eval g:currentJob->job_setoptions({'exit_cb': 'MyExitCb'}) 1532 let g:Ch_exit_job = g:currentJob 1533 call assert_equal('MyExitCb', job_info(g:currentJob)['exit_cb']) 1534endfunc 1535 1536func Test_exit_callback() 1537 call s:run_server('Ch_test_exit_callback') 1538 1539 " wait up to a second for the job to exit 1540 for i in range(100) 1541 if g:Ch_job_exit_ret == 'done' 1542 break 1543 endif 1544 sleep 10m 1545 " calling job_status() triggers the callback 1546 call job_status(g:Ch_exit_job) 1547 endfor 1548 1549 call assert_equal('done', g:Ch_job_exit_ret) 1550 call assert_equal('dead', job_info(g:Ch_exit_job).status) 1551 unlet g:Ch_exit_job 1552endfunc 1553 1554function MyExitTimeCb(job, status) 1555 if job_info(a:job).process == g:exit_cb_val.process 1556 let g:exit_cb_val.end = reltime(g:exit_cb_val.start) 1557 endif 1558 call Resume() 1559endfunction 1560 1561func Test_exit_callback_interval() 1562 CheckFunction reltimefloat 1563 1564 let g:exit_cb_val = {'start': reltime(), 'end': 0, 'process': 0} 1565 let job = [s:python, '-c', 'import time;time.sleep(0.5)']->job_start({'exit_cb': 'MyExitTimeCb'}) 1566 let g:exit_cb_val.process = job_info(job).process 1567 call WaitFor('type(g:exit_cb_val.end) != v:t_number || g:exit_cb_val.end != 0') 1568 let elapsed = reltimefloat(g:exit_cb_val.end) 1569 call assert_true(elapsed > 0.5) 1570 call assert_true(elapsed < 1.0) 1571 1572 " case: unreferenced job, using timer 1573 if !has('timers') 1574 return 1575 endif 1576 1577 let g:exit_cb_val = {'start': reltime(), 'end': 0, 'process': 0} 1578 let g:job = job_start([s:python, '-c', 'import time;time.sleep(0.5)'], {'exit_cb': 'MyExitTimeCb'}) 1579 let g:exit_cb_val.process = job_info(g:job).process 1580 unlet g:job 1581 call Standby(1000) 1582 if type(g:exit_cb_val.end) != v:t_number || g:exit_cb_val.end != 0 1583 let elapsed = reltimefloat(g:exit_cb_val.end) 1584 else 1585 let elapsed = 1.0 1586 endif 1587 call assert_inrange(0.5, 1.0, elapsed) 1588endfunc 1589 1590""""""""" 1591 1592let g:Ch_close_ret = 'alive' 1593function MyCloseCb(ch) 1594 let g:Ch_close_ret = 'closed' 1595endfunc 1596 1597function Ch_test_close_callback(port) 1598 let handle = ch_open(s:localhost . a:port, s:chopt) 1599 if ch_status(handle) == "fail" 1600 call assert_report("Can't open channel") 1601 return 1602 endif 1603 call ch_setoptions(handle, {'close_cb': 'MyCloseCb'}) 1604 1605 call assert_equal('', ch_evalexpr(handle, 'close me')) 1606 call WaitForAssert({-> assert_equal('closed', g:Ch_close_ret)}) 1607endfunc 1608 1609func Test_close_callback() 1610 call s:run_server('Ch_test_close_callback') 1611endfunc 1612 1613func Test_close_callback_ipv6() 1614 CheckIPv6 1615 call Test_close_callback() 1616endfunc 1617 1618function Ch_test_close_partial(port) 1619 let handle = ch_open(s:localhost . a:port, s:chopt) 1620 if ch_status(handle) == "fail" 1621 call assert_report("Can't open channel") 1622 return 1623 endif 1624 let g:Ch_d = {} 1625 func g:Ch_d.closeCb(ch) dict 1626 let self.close_ret = 'closed' 1627 endfunc 1628 call ch_setoptions(handle, {'close_cb': g:Ch_d.closeCb}) 1629 1630 call assert_equal('', ch_evalexpr(handle, 'close me')) 1631 call WaitForAssert({-> assert_equal('closed', g:Ch_d.close_ret)}) 1632 unlet g:Ch_d 1633endfunc 1634 1635func Test_close_partial() 1636 call s:run_server('Ch_test_close_partial') 1637endfunc 1638 1639func Test_close_partial_ipv6() 1640 CheckIPv6 1641 call Test_close_partial() 1642endfunc 1643 1644func Test_job_start_fails() 1645 " this was leaking memory 1646 call assert_fails("call job_start([''])", "E474:") 1647 call assert_fails('call job_start($x)', 'E474:') 1648 call assert_fails('call job_start("")', 'E474:') 1649 call assert_fails('call job_start("ls", {"out_io" : "abc"})', 'E475:') 1650 call assert_fails('call job_start("ls", {"err_io" : "abc"})', 'E475:') 1651 call assert_fails('call job_start("ls", [])', 'E715:') 1652 call assert_fails("call job_start('ls', {'in_top' : -1})", 'E475:') 1653 call assert_fails("call job_start('ls', {'in_bot' : -1})", 'E475:') 1654 call assert_fails("call job_start('ls', {'channel' : -1})", 'E475:') 1655 call assert_fails("call job_start('ls', {'callback' : -1})", 'E921:') 1656 call assert_fails("call job_start('ls', {'out_cb' : -1})", 'E921:') 1657 call assert_fails("call job_start('ls', {'err_cb' : -1})", 'E921:') 1658 call assert_fails("call job_start('ls', {'close_cb' : -1})", 'E921:') 1659 call assert_fails("call job_start('ls', {'exit_cb' : -1})", 'E921:') 1660 call assert_fails("call job_start('ls', {'term_name' : []})", 'E475:') 1661 call assert_fails("call job_start('ls', {'term_finish' : 'run'})", 'E475:') 1662 call assert_fails("call job_start('ls', {'term_api' : []})", 'E475:') 1663 call assert_fails("call job_start('ls', {'stoponexit' : []})", 'E730:') 1664 call assert_fails("call job_start('ls', {'in_io' : 'file'})", 'E920:') 1665 call assert_fails("call job_start('ls', {'out_io' : 'file'})", 'E920:') 1666 call assert_fails("call job_start('ls', {'err_io' : 'file'})", 'E920:') 1667 call assert_fails("call job_start('ls', {'in_mode' : 'abc'})", 'E475:') 1668 call assert_fails("call job_start('ls', {'out_mode' : 'abc'})", 'E475:') 1669 call assert_fails("call job_start('ls', {'err_mode' : 'abc'})", 'E475:') 1670 call assert_fails("call job_start('ls', 1671 \ {'in_io' : 'buffer', 'in_buf' : 99999})", 'E86:') 1672 call assert_fails("call job_start('ls', 1673 \ {'out_io' : 'buffer', 'out_buf' : 99999})", 'E86:') 1674 call assert_fails("call job_start('ls', 1675 \ {'err_io' : 'buffer', 'err_buf' : 99999})", 'E86:') 1676 1677 call assert_fails("call job_start('ls', 1678 \ {'in_io' : 'buffer', 'in_buf' : -1})", 'E475:') 1679 call assert_fails("call job_start('ls', 1680 \ {'out_io' : 'buffer', 'out_buf' : -1})", 'E475:') 1681 call assert_fails("call job_start('ls', 1682 \ {'err_io' : 'buffer', 'err_buf' : -1})", 'E475:') 1683 1684 set nomodifiable 1685 call assert_fails("call job_start('cmd /c dir', 1686 \ {'out_io' : 'buffer', 'out_buf' :" .. bufnr() .. "})", 'E21:') 1687 call assert_fails("call job_start('cmd /c dir', 1688 \ {'err_io' : 'buffer', 'err_buf' :" .. bufnr() .. "})", 'E21:') 1689 set modifiable 1690 1691 call assert_fails("call job_start('ls', {'in_io' : 'buffer'})", 'E915:') 1692 1693 edit! XXX 1694 let bnum = bufnr() 1695 enew 1696 call assert_fails("call job_start('ls', 1697 \ {'in_io' : 'buffer', 'in_buf' : bnum})", 'E918:') 1698 1699 " Empty job tests 1700 " This was crashing on MS-Windows. 1701 call assert_fails('let job = job_start([""])', 'E474:') 1702 call assert_fails('let job = job_start([" "])', 'E474:') 1703 call assert_fails('let job = job_start("")', 'E474:') 1704 call assert_fails('let job = job_start(" ")', 'E474:') 1705 call assert_fails('let job = job_start(["ls", []])', 'E730:') 1706 call assert_fails('call job_setoptions(test_null_job(), {})', 'E916:') 1707 %bw! 1708endfunc 1709 1710func Test_job_stop_immediately() 1711 let g:job = job_start([s:python, '-c', 'import time;time.sleep(10)']) 1712 try 1713 eval g:job->job_stop() 1714 call WaitForAssert({-> assert_equal('dead', job_status(g:job))}) 1715 finally 1716 call job_stop(g:job, 'kill') 1717 unlet g:job 1718 endtry 1719endfunc 1720 1721func Test_null_job_eval() 1722 call assert_fails('eval test_null_job()->eval()', 'E121:') 1723endfunc 1724 1725" This was leaking memory. 1726func Test_partial_in_channel_cycle() 1727 let d = {} 1728 let d.a = function('string', [d]) 1729 try 1730 let d.b = ch_open('nowhere:123', {'close_cb': d.a}) 1731 call test_garbagecollect_now() 1732 catch 1733 call assert_exception('E901:') 1734 endtry 1735 unlet d 1736endfunc 1737 1738func Test_using_freed_memory() 1739 let g:a = job_start(['ls']) 1740 sleep 10m 1741 call test_garbagecollect_now() 1742endfunc 1743 1744func Test_collapse_buffers() 1745 CheckExecutable cat 1746 1747 sp test_channel.vim 1748 let g:linecount = line('$') 1749 close 1750 split testout 1751 1,$delete 1752 call job_start('cat test_channel.vim', {'out_io': 'buffer', 'out_name': 'testout'}) 1753 call WaitForAssert({-> assert_inrange(g:linecount, g:linecount + 1, line('$'))}) 1754 bwipe! 1755endfunc 1756 1757func Test_write_to_deleted_buffer() 1758 CheckExecutable echo 1759 CheckFeature quickfix 1760 1761 let job = job_start('echo hello', {'out_io': 'buffer', 'out_name': 'test_buffer', 'out_msg': 0}) 1762 let bufnr = bufnr('test_buffer') 1763 call WaitForAssert({-> assert_equal(['hello'], getbufline(bufnr, 1, '$'))}) 1764 call assert_equal('nofile', getbufvar(bufnr, '&buftype')) 1765 call assert_equal('hide', getbufvar(bufnr, '&bufhidden')) 1766 1767 bdel test_buffer 1768 call assert_equal([], getbufline(bufnr, 1, '$')) 1769 1770 let job = job_start('echo hello', {'out_io': 'buffer', 'out_name': 'test_buffer', 'out_msg': 0}) 1771 call WaitForAssert({-> assert_equal(['hello'], getbufline(bufnr, 1, '$'))}) 1772 call assert_equal('nofile', getbufvar(bufnr, '&buftype')) 1773 call assert_equal('hide', getbufvar(bufnr, '&bufhidden')) 1774 1775 bwipe! test_buffer 1776endfunc 1777 1778func Test_cmd_parsing() 1779 CheckUnix 1780 1781 call assert_false(filereadable("file with space")) 1782 let job = job_start('touch "file with space"') 1783 call WaitForAssert({-> assert_true(filereadable("file with space"))}) 1784 call delete("file with space") 1785 1786 let job = job_start('touch file\ with\ space') 1787 call WaitForAssert({-> assert_true(filereadable("file with space"))}) 1788 call delete("file with space") 1789endfunc 1790 1791func Test_raw_passes_nul() 1792 CheckExecutable cat 1793 1794 " Test lines from the job containing NUL are stored correctly in a buffer. 1795 new 1796 call setline(1, ["asdf\nasdf", "xxx\n", "\nyyy"]) 1797 w! Xtestread 1798 bwipe! 1799 split testout 1800 1,$delete 1801 call job_start('cat Xtestread', {'out_io': 'buffer', 'out_name': 'testout'}) 1802 call WaitFor('line("$") > 2') 1803 call assert_equal("asdf\nasdf", getline(1)) 1804 call assert_equal("xxx\n", getline(2)) 1805 call assert_equal("\nyyy", getline(3)) 1806 1807 call delete('Xtestread') 1808 bwipe! 1809 1810 " Test lines from a buffer with NUL bytes are written correctly to the job. 1811 new mybuffer 1812 call setline(1, ["asdf\nasdf", "xxx\n", "\nyyy"]) 1813 let g:Ch_job = job_start('cat', {'in_io': 'buffer', 'in_name': 'mybuffer', 'out_io': 'file', 'out_name': 'Xtestwrite'}) 1814 call WaitForAssert({-> assert_equal("dead", job_status(g:Ch_job))}) 1815 bwipe! 1816 split Xtestwrite 1817 call assert_equal("asdf\nasdf", getline(1)) 1818 call assert_equal("xxx\n", getline(2)) 1819 call assert_equal("\nyyy", getline(3)) 1820 call assert_equal(-1, match(s:get_resources(), '\(^\|/\)Xtestwrite$')) 1821 1822 call delete('Xtestwrite') 1823 bwipe! 1824endfunc 1825 1826func Test_read_nonl_line() 1827 let g:linecount = 0 1828 let arg = 'import sys;sys.stdout.write("1\n2\n3")' 1829 call job_start([s:python, '-c', arg], {'callback': {-> execute('let g:linecount += 1')}}) 1830 call WaitForAssert({-> assert_equal(3, g:linecount)}) 1831 unlet g:linecount 1832endfunc 1833 1834func Test_read_nonl_in_close_cb() 1835 func s:close_cb(ch) 1836 while ch_status(a:ch) == 'buffered' 1837 let g:out .= ch_read(a:ch) 1838 endwhile 1839 endfunc 1840 1841 let g:out = '' 1842 let arg = 'import sys;sys.stdout.write("1\n2\n3")' 1843 call job_start([s:python, '-c', arg], {'close_cb': function('s:close_cb')}) 1844 call test_garbagecollect_now() 1845 call WaitForAssert({-> assert_equal('123', g:out)}) 1846 unlet g:out 1847 delfunc s:close_cb 1848endfunc 1849 1850func Test_read_from_terminated_job() 1851 let g:linecount = 0 1852 let arg = 'import os,sys;os.close(1);sys.stderr.write("test\n")' 1853 call job_start([s:python, '-c', arg], {'callback': {-> execute('let g:linecount += 1')}}) 1854 call WaitForAssert({-> assert_equal(1, g:linecount)}) 1855 call test_garbagecollect_now() 1856 unlet g:linecount 1857endfunc 1858 1859func Test_job_start_windows() 1860 CheckMSWindows 1861 1862 " Check that backslash in $COMSPEC is handled properly. 1863 let g:echostr = '' 1864 let cmd = $COMSPEC . ' /c echo 123' 1865 let job = job_start(cmd, {'callback': {ch,msg -> execute(":let g:echostr .= msg")}}) 1866 let info = job_info(job) 1867 call assert_equal([$COMSPEC, '/c', 'echo', '123'], info.cmd) 1868 1869 call WaitForAssert({-> assert_equal("123", g:echostr)}) 1870 unlet g:echostr 1871endfunc 1872 1873func Test_env() 1874 let g:envstr = '' 1875 if has('win32') 1876 let cmd = ['cmd', '/c', 'echo %FOO%'] 1877 else 1878 let cmd = [&shell, &shellcmdflag, 'echo $FOO'] 1879 endif 1880 call assert_fails('call job_start(cmd, {"env": 1})', 'E475:') 1881 call job_start(cmd, {'callback': {ch,msg -> execute(":let g:envstr .= msg")}, 'env': {'FOO': 'bar'}}) 1882 call WaitForAssert({-> assert_equal("bar", g:envstr)}) 1883 unlet g:envstr 1884endfunc 1885 1886func Test_cwd() 1887 let g:envstr = '' 1888 if has('win32') 1889 let expect = $TEMP 1890 let cmd = ['cmd', '/c', 'echo %CD%'] 1891 else 1892 let expect = $HOME 1893 let cmd = ['pwd'] 1894 endif 1895 let job = job_start(cmd, {'callback': {ch,msg -> execute(":let g:envstr .= msg")}, 'cwd': expect}) 1896 try 1897 call WaitForAssert({-> assert_notequal("", g:envstr)}) 1898 let expect = substitute(expect, '[/\\]$', '', '') 1899 let g:envstr = substitute(g:envstr, '[/\\]$', '', '') 1900 if $CI != '' && stridx(g:envstr, '/private/') == 0 1901 let g:envstr = g:envstr[8:] 1902 endif 1903 call assert_equal(expect, g:envstr) 1904 finally 1905 call job_stop(job) 1906 unlet g:envstr 1907 endtry 1908endfunc 1909 1910function Ch_test_close_lambda(port) 1911 let handle = ch_open(s:localhost . a:port, s:chopt) 1912 if ch_status(handle) == "fail" 1913 call assert_report("Can't open channel") 1914 return 1915 endif 1916 let g:Ch_close_ret = '' 1917 call ch_setoptions(handle, {'close_cb': {ch -> execute("let g:Ch_close_ret = 'closed'")}}) 1918 call test_garbagecollect_now() 1919 1920 call assert_equal('', ch_evalexpr(handle, 'close me')) 1921 call WaitForAssert({-> assert_equal('closed', g:Ch_close_ret)}) 1922endfunc 1923 1924func Test_close_lambda() 1925 call s:run_server('Ch_test_close_lambda') 1926endfunc 1927 1928func Test_close_lambda_ipv6() 1929 CheckIPv6 1930 call Test_close_lambda() 1931endfunc 1932 1933func s:test_list_args(cmd, out, remove_lf) 1934 try 1935 let g:out = '' 1936 let job = job_start([s:python, '-c', a:cmd], {'callback': {ch, msg -> execute('let g:out .= msg')}, 'out_mode': 'raw'}) 1937 call WaitFor('"" != g:out') 1938 if has('win32') 1939 let g:out = substitute(g:out, '\r', '', 'g') 1940 endif 1941 if a:remove_lf 1942 let g:out = substitute(g:out, '\n$', '', 'g') 1943 endif 1944 call assert_equal(a:out, g:out) 1945 finally 1946 call job_stop(job) 1947 unlet g:out 1948 endtry 1949endfunc 1950 1951func Test_list_args() 1952 call s:test_list_args('import sys;sys.stdout.write("hello world")', "hello world", 0) 1953 call s:test_list_args('import sys;sys.stdout.write("hello\nworld")', "hello\nworld", 0) 1954 call s:test_list_args('import sys;sys.stdout.write(''hello\nworld'')', "hello\nworld", 0) 1955 call s:test_list_args('import sys;sys.stdout.write(''hello"world'')', "hello\"world", 0) 1956 call s:test_list_args('import sys;sys.stdout.write(''hello^world'')', "hello^world", 0) 1957 call s:test_list_args('import sys;sys.stdout.write("hello&&world")', "hello&&world", 0) 1958 call s:test_list_args('import sys;sys.stdout.write(''hello\\world'')', "hello\\world", 0) 1959 call s:test_list_args('import sys;sys.stdout.write(''hello\\\\world'')', "hello\\\\world", 0) 1960 call s:test_list_args('import sys;sys.stdout.write("hello\"world\"")', 'hello"world"', 0) 1961 call s:test_list_args('import sys;sys.stdout.write("h\"ello worl\"d")', 'h"ello worl"d', 0) 1962 call s:test_list_args('import sys;sys.stdout.write("h\"e\\\"llo wor\\\"l\"d")', 'h"e\"llo wor\"l"d', 0) 1963 call s:test_list_args('import sys;sys.stdout.write("h\"e\\\"llo world")', 'h"e\"llo world', 0) 1964 call s:test_list_args('import sys;sys.stdout.write("hello\tworld")', "hello\tworld", 0) 1965 1966 " tests which not contain spaces in the argument 1967 call s:test_list_args('print("hello\nworld")', "hello\nworld", 1) 1968 call s:test_list_args('print(''hello\nworld'')', "hello\nworld", 1) 1969 call s:test_list_args('print(''hello"world'')', "hello\"world", 1) 1970 call s:test_list_args('print(''hello^world'')', "hello^world", 1) 1971 call s:test_list_args('print("hello&&world")', "hello&&world", 1) 1972 call s:test_list_args('print(''hello\\world'')', "hello\\world", 1) 1973 call s:test_list_args('print(''hello\\\\world'')', "hello\\\\world", 1) 1974 call s:test_list_args('print("hello\"world\"")', 'hello"world"', 1) 1975 call s:test_list_args('print("hello\tworld")', "hello\tworld", 1) 1976endfunc 1977 1978func Test_keep_pty_open() 1979 CheckUnix 1980 1981 let job = job_start(s:python . ' -c "import time;time.sleep(0.2)"', 1982 \ {'out_io': 'null', 'err_io': 'null', 'pty': 1}) 1983 let elapsed = WaitFor({-> job_status(job) ==# 'dead'}) 1984 call assert_inrange(200, 1000, elapsed) 1985 call job_stop(job) 1986endfunc 1987 1988func Test_job_start_in_timer() 1989 CheckFeature timers 1990 CheckFunction reltimefloat 1991 1992 func OutCb(chan, msg) 1993 let g:val += 1 1994 endfunc 1995 1996 func ExitCb(job, status) 1997 let g:val += 1 1998 call Resume() 1999 endfunc 2000 2001 func TimerCb(timer) 2002 if has('win32') 2003 let cmd = ['cmd', '/c', 'echo.'] 2004 else 2005 let cmd = ['echo'] 2006 endif 2007 let g:job = job_start(cmd, {'out_cb': 'OutCb', 'exit_cb': 'ExitCb'}) 2008 call substitute(repeat('a', 100000), '.', '', 'g') 2009 endfunc 2010 2011 " We should be interrupted before 'updatetime' elapsed. 2012 let g:val = 0 2013 call timer_start(1, 'TimerCb') 2014 let elapsed = Standby(&ut) 2015 call assert_inrange(1, &ut / 2, elapsed) 2016 2017 " Wait for both OutCb() and ExitCb() to have been called before deleting 2018 " them. 2019 call WaitForAssert({-> assert_equal(2, g:val)}) 2020 call job_stop(g:job) 2021 2022 delfunc OutCb 2023 delfunc ExitCb 2024 delfunc TimerCb 2025 unlet! g:val 2026 unlet! g:job 2027endfunc 2028 2029func Test_raw_large_data() 2030 try 2031 let g:out = '' 2032 let job = job_start(s:python . " test_channel_pipe.py", 2033 \ {'mode': 'raw', 'drop': 'never', 'noblock': 1, 2034 \ 'callback': {ch, msg -> execute('let g:out .= msg')}}) 2035 2036 let outlen = 79999 2037 let want = repeat('X', outlen) . "\n" 2038 eval job->ch_sendraw(want) 2039 call WaitFor({-> len(g:out) >= outlen}, 10000) 2040 call WaitForAssert({-> assert_equal("dead", job_status(job))}) 2041 call assert_equal(want, substitute(g:out, '\r', '', 'g')) 2042 finally 2043 call job_stop(job) 2044 unlet g:out 2045 endtry 2046endfunc 2047 2048func Test_no_hang_windows() 2049 CheckMSWindows 2050 2051 try 2052 let job = job_start(s:python . " test_channel_pipe.py busy", 2053 \ {'mode': 'raw', 'drop': 'never', 'noblock': 0}) 2054 call assert_fails('call ch_sendraw(job, repeat("X", 80000))', 'E631:') 2055 finally 2056 call job_stop(job) 2057 endtry 2058endfunc 2059 2060func Test_job_exitval_and_termsig() 2061 CheckUnix 2062 2063 " Terminate job normally 2064 let cmd = ['echo'] 2065 let job = job_start(cmd) 2066 call WaitForAssert({-> assert_equal("dead", job_status(job))}) 2067 let info = job_info(job) 2068 call assert_equal(0, info.exitval) 2069 call assert_equal("", info.termsig) 2070 2071 " Terminate job by signal 2072 let cmd = ['sleep', '10'] 2073 let job = job_start(cmd) 2074 " 10m usually works but 50m is needed when running Valgrind 2075 sleep 50m 2076 call job_stop(job) 2077 call WaitForAssert({-> assert_equal("dead", job_status(job))}) 2078 let info = job_info(job) 2079 call assert_equal(-1, info.exitval) 2080 call assert_equal("term", info.termsig) 2081endfunc 2082 2083func Test_job_tty_in_out() 2084 CheckUnix 2085 2086 call writefile(['test'], 'Xtestin') 2087 let in_opts = [{}, 2088 \ {'in_io': 'null'}, 2089 \ {'in_io': 'file', 'in_name': 'Xtestin'}] 2090 let out_opts = [{}, 2091 \ {'out_io': 'null'}, 2092 \ {'out_io': 'file', 'out_name': 'Xtestout'}] 2093 let err_opts = [{}, 2094 \ {'err_io': 'null'}, 2095 \ {'err_io': 'file', 'err_name': 'Xtesterr'}, 2096 \ {'err_io': 'out'}] 2097 let opts = [] 2098 2099 for in_opt in in_opts 2100 let x = copy(in_opt) 2101 for out_opt in out_opts 2102 let x = extend(copy(x), out_opt) 2103 for err_opt in err_opts 2104 let x = extend(copy(x), err_opt) 2105 let opts += [extend({'pty': 1}, x)] 2106 endfor 2107 endfor 2108 endfor 2109 2110 for opt in opts 2111 let job = job_start('echo', opt) 2112 let info = job_info(job) 2113 let msg = printf('option={"in_io": "%s", "out_io": "%s", "err_io": "%s"}', 2114 \ get(opt, 'in_io', 'tty'), 2115 \ get(opt, 'out_io', 'tty'), 2116 \ get(opt, 'err_io', 'tty')) 2117 2118 if !has_key(opt, 'in_io') || !has_key(opt, 'out_io') || !has_key(opt, 'err_io') 2119 call assert_notequal('', info.tty_in, msg) 2120 else 2121 call assert_equal('', info.tty_in, msg) 2122 endif 2123 call assert_equal(info.tty_in, info.tty_out, msg) 2124 2125 call WaitForAssert({-> assert_equal('dead', job_status(job))}) 2126 endfor 2127 2128 call delete('Xtestin') 2129 call delete('Xtestout') 2130 call delete('Xtesterr') 2131endfunc 2132 2133" Do this last, it stops any channel log. 2134func Test_zz_nl_err_to_out_pipe() 2135 2136 eval 'Xlog'->ch_logfile() 2137 call ch_log('Test_zz_nl_err_to_out_pipe()') 2138 let job = job_start(s:python . " test_channel_pipe.py", {'err_io': 'out'}) 2139 call assert_equal("run", job_status(job)) 2140 try 2141 let handle = job_getchannel(job) 2142 call ch_sendraw(handle, "echo something\n") 2143 call assert_equal("something", ch_readraw(handle)) 2144 2145 call ch_sendraw(handle, "echoerr wrong\n") 2146 call assert_equal("wrong", ch_readraw(handle)) 2147 finally 2148 call job_stop(job) 2149 call ch_logfile('') 2150 let loglines = readfile('Xlog') 2151 call assert_true(len(loglines) > 10) 2152 let found_test = 0 2153 let found_send = 0 2154 let found_recv = 0 2155 let found_stop = 0 2156 for l in loglines 2157 if l =~ 'Test_zz_nl_err_to_out_pipe' 2158 let found_test = 1 2159 endif 2160 if l =~ 'SEND on.*echo something' 2161 let found_send = 1 2162 endif 2163 if l =~ 'RECV on.*something' 2164 let found_recv = 1 2165 endif 2166 if l =~ 'Stopping job with' 2167 let found_stop = 1 2168 endif 2169 endfor 2170 call assert_equal(1, found_test) 2171 call assert_equal(1, found_send) 2172 call assert_equal(1, found_recv) 2173 call assert_equal(1, found_stop) 2174 " On MS-Windows need to sleep for a moment to be able to delete the file. 2175 sleep 10m 2176 call delete('Xlog') 2177 endtry 2178endfunc 2179 2180" Do this last, it stops any channel log. 2181func Test_zz_ch_log() 2182 call ch_logfile('Xlog', 'w') 2183 call ch_log('hello there') 2184 call ch_log('%s%s') 2185 call ch_logfile('') 2186 let text = readfile('Xlog') 2187 call assert_match("hello there", text[1]) 2188 call assert_match("%s%s", text[2]) 2189 call mkdir("Xdir1") 2190 call assert_fails("call ch_logfile('Xdir1')", 'E484:') 2191 cal delete("Xdir1", 'd') 2192 call delete('Xlog') 2193endfunc 2194 2195func Test_issue_5150() 2196 if has('win32') 2197 let cmd = 'cmd /c pause' 2198 else 2199 let cmd = 'grep foo' 2200 endif 2201 2202 let g:job = job_start(cmd, {}) 2203 sleep 50m " give the job time to start 2204 call job_stop(g:job) 2205 call WaitForAssert({-> assert_equal(-1, job_info(g:job).exitval)}) 2206 2207 let g:job = job_start(cmd, {}) 2208 sleep 50m 2209 call job_stop(g:job, 'term') 2210 call WaitForAssert({-> assert_equal(-1, job_info(g:job).exitval)}) 2211 2212 let g:job = job_start(cmd, {}) 2213 sleep 50m 2214 call job_stop(g:job, 'kill') 2215 call WaitForAssert({-> assert_equal(-1, job_info(g:job).exitval)}) 2216endfunc 2217 2218func Test_issue_5485() 2219 let $VAR1 = 'global' 2220 let g:Ch_reply = "" 2221 let l:job = job_start([&shell, &shellcmdflag, has('win32') ? 'echo %VAR1% %VAR2%' : 'echo $VAR1 $VAR2'], {'env': {'VAR1': 'local', 'VAR2': 'local'}, 'callback': 'Ch_handler'}) 2222 let g:Ch_job = l:job 2223 call WaitForAssert({-> assert_equal("local local", trim(g:Ch_reply))}) 2224 unlet $VAR1 2225endfunc 2226 2227func Test_job_trailing_space_unix() 2228 CheckUnix 2229 CheckExecutable cat 2230 2231 let job = job_start("cat ", #{in_io: 'null'}) 2232 call WaitForAssert({-> assert_equal("dead", job_status(job))}) 2233 call assert_equal(0, job_info(job).exitval) 2234endfunc 2235 2236func Test_ch_getbufnr() 2237 let ch = test_null_channel() 2238 call assert_equal(-1, ch_getbufnr(ch, 'in')) 2239 call assert_equal(-1, ch_getbufnr(ch, 'out')) 2240 call assert_equal(-1, ch_getbufnr(ch, 'err')) 2241 call assert_equal(-1, ch_getbufnr(ch, '')) 2242endfunc 2243 2244" Test for unsupported options passed to ch_status() 2245func Test_invalid_job_chan_options() 2246 let ch = test_null_channel() 2247 let invalid_opts = [ 2248 \ {'in_io' : 'null'}, 2249 \ {'out_io' : 'null'}, 2250 \ {'err_io' : 'null'}, 2251 \ {'mode' : 'json'}, 2252 \ {'out_mode' : 'json'}, 2253 \ {'err_mode' : 'json'}, 2254 \ {'noblock' : 1}, 2255 \ {'in_name' : '/a/b'}, 2256 \ {'pty' : 1}, 2257 \ {'in_buf' : 1}, 2258 \ {'out_buf' : 1}, 2259 \ {'err_buf' : 1}, 2260 \ {'out_modifiable' : 1}, 2261 \ {'err_modifiable' : 1}, 2262 \ {'out_msg' : 1}, 2263 \ {'err_msg' : 1}, 2264 \ {'in_top' : 1}, 2265 \ {'in_bot' : 1}, 2266 \ {'channel' : ch}, 2267 \ {'callback' : ''}, 2268 \ {'out_cb' : ''}, 2269 \ {'err_cb' : ''}, 2270 \ {'close_cb' : ''}, 2271 \ {'exit_cb' : ''}, 2272 \ {'term_opencmd' : ''}, 2273 \ {'eof_chars' : ''}, 2274 \ {'term_rows' : 10}, 2275 \ {'term_cols' : 10}, 2276 \ {'vertical' : 0}, 2277 \ {'curwin' : 1}, 2278 \ {'bufnr' : 1}, 2279 \ {'hidden' : 0}, 2280 \ {'norestore' : 0}, 2281 \ {'term_kill' : 'kill'}, 2282 \ {'tty_type' : ''}, 2283 \ {'term_highlight' : ''}, 2284 \ {'env' : {}}, 2285 \ {'cwd' : ''}, 2286 \ {'timeout' : 0}, 2287 \ {'out_timeout' : 0}, 2288 \ {'err_timeout' : 0}, 2289 \ {'id' : 0}, 2290 \ {'stoponexit' : ''}, 2291 \ {'block_write' : 1} 2292 \ ] 2293 if has('gui') 2294 call add(invalid_opts, {'ansi_colors' : []}) 2295 endif 2296 2297 for opt in invalid_opts 2298 call assert_fails("let x = ch_status(ch, opt)", 'E475:') 2299 endfor 2300 call assert_equal('fail', ch_status(ch, test_null_dict())) 2301endfunc 2302 2303" Test for passing the command and the arguments as List on MS-Windows 2304func Test_job_with_list_args() 2305 CheckMSWindows 2306 2307 enew! 2308 let bnum = bufnr() 2309 let job = job_start(['cmd', '/c', 'echo', 'Hello', 'World'], {'out_io' : 'buffer', 'out_buf' : bnum}) 2310 call WaitForAssert({-> assert_equal("dead", job_status(job))}) 2311 call assert_equal('Hello World', getline(1)) 2312 %bw! 2313endfunc 2314 2315func ExitCb_cb_with_input(job, status) 2316 call feedkeys(":\<C-u>echo input('', 'default')\<CR>\<CR>", 'nx') 2317 call assert_equal('default', Screenline(&lines)) 2318 let g:wait_exit_cb = 0 2319endfunc 2320 2321func Test_cb_with_input() 2322 let g:wait_exit_cb = 1 2323 2324 if has('win32') 2325 let cmd = 'cmd /c echo "Vim''s test"' 2326 else 2327 let cmd = 'echo "Vim''s test"' 2328 endif 2329 2330 let job = job_start(cmd, {'out_cb': 'ExitCb_cb_with_input'}) 2331 call WaitFor({-> job_status(job) == "dead"}) 2332 call WaitForAssert({-> assert_equal(0, g:wait_exit_cb)}) 2333 2334 unlet g:wait_exit_cb 2335endfunc 2336 2337function s:HandleBufEnter() abort 2338 let queue = [] 2339 let job = job_start(['date'], {'callback': { j, d -> add(queue, d) }}) 2340 while empty(queue) 2341 sleep! 10m 2342 endwhile 2343endfunction 2344 2345func Test_parse_messages_in_autocmd() 2346 CheckUnix 2347 2348 " Check that in the BufEnter autocommand events are being handled 2349 augroup bufenterjob 2350 autocmd! 2351 autocmd BufEnter Xbufenterjob call s:HandleBufEnter() 2352 augroup END 2353 2354 only 2355 split Xbufenterjob 2356 wincmd p 2357 redraw 2358 2359 close 2360 augroup bufenterjob 2361 autocmd! 2362 augroup END 2363endfunc 2364 2365func Test_job_start_with_invalid_argument() 2366 call assert_fails('call job_start([0zff])', 'E976:') 2367endfunc 2368 2369 2370" vim: shiftwidth=2 sts=2 expandtab 2371