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 let g:test_is_flaky = 1 799 enew! 800 let test_lines = ['one', 'two'] 801 call setline(1, test_lines) 802 let options = {'out_io': 'buffer'} 803 let options['out_name'] = 'buffer-output' 804 let options['out_msg'] = 0 805 split buffer-output 806 let job = job_start(s:python . " test_channel_write.py", options) 807 call assert_equal("run", job_status(job)) 808 try 809 call WaitForAssert({-> assert_equal(3, line('$'))}) 810 quit! 811 sleep 100m 812 " Make sure the write didn't happen to the wrong buffer. 813 call assert_equal(test_lines, getline(1, line('$'))) 814 call assert_equal(-1, bufwinnr('buffer-output')) 815 sbuf buffer-output 816 call assert_notequal(-1, bufwinnr('buffer-output')) 817 sleep 100m 818 close " no more writes 819 bwipe! 820 finally 821 call job_stop(job) 822 endtry 823endfunc 824 825func Run_test_pipe_err_to_buffer(use_name, nomod, do_msg) 826 let options = {'err_io': 'buffer'} 827 let expected = ['', 'line one', 'line two', 'this', 'AND this'] 828 if a:use_name 829 let options['err_name'] = 'pipe-err' 830 if a:do_msg 831 let expected[0] = 'Reading from channel error...' 832 else 833 let options['err_msg'] = 0 834 call remove(expected, 0) 835 endif 836 else 837 sp pipe-err 838 let options['err_buf'] = bufnr('%') 839 quit 840 call remove(expected, 0) 841 endif 842 if a:nomod 843 let options['err_modifiable'] = 0 844 endif 845 let job = job_start(s:python . " test_channel_pipe.py", options) 846 call assert_equal("run", job_status(job)) 847 try 848 let handle = job_getchannel(job) 849 call ch_sendraw(handle, "echoerr line one\n") 850 call ch_sendraw(handle, "echoerr line two\n") 851 call ch_sendraw(handle, "doubleerr this\n") 852 call ch_sendraw(handle, "quit\n") 853 sp pipe-err 854 call WaitForAssert({-> assert_equal(expected, getline(1, '$'))}) 855 if a:nomod 856 call assert_equal(0, &modifiable) 857 else 858 call assert_equal(1, &modifiable) 859 endif 860 bwipe! 861 finally 862 call job_stop(job) 863 endtry 864endfunc 865 866func Test_pipe_err_to_buffer_name() 867 call Run_test_pipe_err_to_buffer(1, 0, 1) 868endfunc 869 870func Test_pipe_err_to_buffer_nr() 871 call Run_test_pipe_err_to_buffer(0, 0, 1) 872endfunc 873 874func Test_pipe_err_to_buffer_name_nomod() 875 call Run_test_pipe_err_to_buffer(1, 1, 1) 876endfunc 877 878func Test_pipe_err_to_buffer_name_nomsg() 879 call Run_test_pipe_err_to_buffer(1, 0, 0) 880endfunc 881 882func Test_pipe_both_to_buffer() 883 let job = job_start(s:python . " test_channel_pipe.py", 884 \ {'out_io': 'buffer', 'out_name': 'pipe-err', 'err_io': 'out'}) 885 call assert_equal("run", job_status(job)) 886 let handle = job_getchannel(job) 887 call assert_equal(bufnr('pipe-err'), ch_getbufnr(handle, 'out')) 888 call assert_equal(bufnr('pipe-err'), ch_getbufnr(handle, 'err')) 889 try 890 call ch_sendraw(handle, "echo line one\n") 891 call ch_sendraw(handle, "echoerr line two\n") 892 call ch_sendraw(handle, "double this\n") 893 call ch_sendraw(handle, "doubleerr that\n") 894 call ch_sendraw(handle, "quit\n") 895 sp pipe-err 896 call WaitForAssert({-> assert_equal(['Reading from channel output...', 'line one', 'line two', 'this', 'AND this', 'that', 'AND that', 'Goodbye!'], getline(1, '$'))}) 897 bwipe! 898 finally 899 call job_stop(job) 900 endtry 901endfunc 902 903func Run_test_pipe_from_buffer(use_name) 904 sp pipe-input 905 call setline(1, ['echo one', 'echo two', 'echo three']) 906 let options = {'in_io': 'buffer', 'block_write': 1} 907 if a:use_name 908 let options['in_name'] = 'pipe-input' 909 else 910 let options['in_buf'] = bufnr('%') 911 endif 912 913 let job = job_start(s:python . " test_channel_pipe.py", options) 914 call assert_equal("run", job_status(job)) 915 if has('unix') && !a:use_name 916 call assert_equal(bufnr('%'), ch_getbufnr(job, 'in')) 917 endif 918 try 919 let handle = job_getchannel(job) 920 call assert_equal('one', ch_read(handle)) 921 call assert_equal('two', ch_read(handle)) 922 call assert_equal('three', ch_read(handle)) 923 bwipe! 924 finally 925 call job_stop(job) 926 endtry 927endfunc 928 929func Test_pipe_from_buffer_name() 930 call Run_test_pipe_from_buffer(1) 931endfunc 932 933func Test_pipe_from_buffer_nr() 934 call Run_test_pipe_from_buffer(0) 935endfunc 936 937func Run_pipe_through_sort(all, use_buffer) 938 CheckExecutable sort 939 let g:test_is_flaky = 1 940 941 let options = {'out_io': 'buffer', 'out_name': 'sortout'} 942 if a:use_buffer 943 split sortin 944 call setline(1, ['ccc', 'aaa', 'ddd', 'bbb', 'eee']) 945 let options.in_io = 'buffer' 946 let options.in_name = 'sortin' 947 endif 948 if !a:all 949 let options.in_top = 2 950 let options.in_bot = 4 951 endif 952 let job = job_start('sort', options) 953 954 if !a:use_buffer 955 call assert_equal("run", job_status(job)) 956 call ch_sendraw(job, "ccc\naaa\nddd\nbbb\neee\n") 957 eval job->ch_close_in() 958 endif 959 960 call WaitForAssert({-> assert_equal("dead", job_status(job))}) 961 962 sp sortout 963 call WaitFor('line("$") > 3') 964 call assert_equal('Reading from channel output...', getline(1)) 965 if a:all 966 call assert_equal(['aaa', 'bbb', 'ccc', 'ddd', 'eee'], getline(2, 6)) 967 else 968 call assert_equal(['aaa', 'bbb', 'ddd'], getline(2, 4)) 969 endif 970 971 call job_stop(job) 972 if a:use_buffer 973 bwipe! sortin 974 endif 975 bwipe! sortout 976endfunc 977 978func Test_pipe_through_sort_all() 979 call Run_pipe_through_sort(1, 1) 980endfunc 981 982func Test_pipe_through_sort_some() 983 call Run_pipe_through_sort(0, 1) 984endfunc 985 986func Test_pipe_through_sort_feed() 987 call Run_pipe_through_sort(1, 0) 988endfunc 989 990func Test_pipe_to_nameless_buffer() 991 let job = job_start(s:python . " test_channel_pipe.py", 992 \ {'out_io': 'buffer'}) 993 call assert_equal("run", job_status(job)) 994 try 995 let handle = job_getchannel(job) 996 call ch_sendraw(handle, "echo line one\n") 997 call ch_sendraw(handle, "echo line two\n") 998 exe handle->ch_getbufnr("out") .. 'sbuf' 999 call WaitFor('line("$") >= 3') 1000 call assert_equal(['Reading from channel output...', 'line one', 'line two'], getline(1, '$')) 1001 bwipe! 1002 finally 1003 call job_stop(job) 1004 endtry 1005endfunc 1006 1007func Test_pipe_to_buffer_json() 1008 CheckFunction reltimefloat 1009 1010 let job = job_start(s:python . " test_channel_pipe.py", 1011 \ {'out_io': 'buffer', 'out_mode': 'json'}) 1012 call assert_equal("run", job_status(job)) 1013 try 1014 let handle = job_getchannel(job) 1015 call ch_sendraw(handle, "echo [0, \"hello\"]\n") 1016 call ch_sendraw(handle, "echo [-2, 12.34]\n") 1017 exe ch_getbufnr(handle, "out") . 'sbuf' 1018 call WaitFor('line("$") >= 3') 1019 call assert_equal(['Reading from channel output...', '[0,"hello"]', '[-2,12.34]'], getline(1, '$')) 1020 bwipe! 1021 finally 1022 call job_stop(job) 1023 endtry 1024endfunc 1025 1026" Wait a little while for the last line, minus "offset", to equal "line". 1027func s:wait_for_last_line(line, offset) 1028 for i in range(100) 1029 if getline(line('$') - a:offset) == a:line 1030 break 1031 endif 1032 sleep 10m 1033 endfor 1034endfunc 1035 1036func Test_pipe_io_two_buffers() 1037 " Create two buffers, one to read from and one to write to. 1038 split pipe-output 1039 set buftype=nofile 1040 split pipe-input 1041 set buftype=nofile 1042 1043 let job = job_start(s:python . " test_channel_pipe.py", 1044 \ {'in_io': 'buffer', 'in_name': 'pipe-input', 'in_top': 0, 1045 \ 'out_io': 'buffer', 'out_name': 'pipe-output', 1046 \ 'block_write': 1}) 1047 call assert_equal("run", job_status(job)) 1048 try 1049 exe "normal Gaecho hello\<CR>" 1050 exe bufwinnr('pipe-output') . "wincmd w" 1051 call s:wait_for_last_line('hello', 0) 1052 call assert_equal('hello', getline('$')) 1053 1054 exe bufwinnr('pipe-input') . "wincmd w" 1055 exe "normal Gadouble this\<CR>" 1056 exe bufwinnr('pipe-output') . "wincmd w" 1057 call s:wait_for_last_line('AND this', 0) 1058 call assert_equal('this', getline(line('$') - 1)) 1059 call assert_equal('AND this', getline('$')) 1060 1061 bwipe! 1062 exe bufwinnr('pipe-input') . "wincmd w" 1063 bwipe! 1064 finally 1065 call job_stop(job) 1066 endtry 1067endfunc 1068 1069func Test_pipe_io_one_buffer() 1070 " Create one buffer to read from and to write to. 1071 split pipe-io 1072 set buftype=nofile 1073 1074 let job = job_start(s:python . " test_channel_pipe.py", 1075 \ {'in_io': 'buffer', 'in_name': 'pipe-io', 'in_top': 0, 1076 \ 'out_io': 'buffer', 'out_name': 'pipe-io', 1077 \ 'block_write': 1}) 1078 call assert_equal("run", job_status(job)) 1079 try 1080 exe "normal Goecho hello\<CR>" 1081 call s:wait_for_last_line('hello', 1) 1082 call assert_equal('hello', getline(line('$') - 1)) 1083 1084 exe "normal Gadouble this\<CR>" 1085 call s:wait_for_last_line('AND this', 1) 1086 call assert_equal('this', getline(line('$') - 2)) 1087 call assert_equal('AND this', getline(line('$') - 1)) 1088 1089 bwipe! 1090 finally 1091 call job_stop(job) 1092 endtry 1093endfunc 1094 1095func Test_write_to_buffer_and_scroll() 1096 CheckScreendump 1097 1098 let lines =<< trim END 1099 new Xscrollbuffer 1100 call setline(1, range(1, 200)) 1101 $ 1102 redraw 1103 wincmd w 1104 call deletebufline('Xscrollbuffer', 1, '$') 1105 if has('win32') 1106 let cmd = ['cmd', '/c', 'echo sometext'] 1107 else 1108 let cmd = [&shell, &shellcmdflag, 'echo sometext'] 1109 endif 1110 call job_start(cmd, #{out_io: 'buffer', out_name: 'Xscrollbuffer'}) 1111 END 1112 call writefile(lines, 'XtestBufferScroll') 1113 let buf = RunVimInTerminal('-S XtestBufferScroll', #{rows: 10}) 1114 call TermWait(buf, 50) 1115 call VerifyScreenDump(buf, 'Test_job_buffer_scroll_1', {}) 1116 1117 " clean up 1118 call StopVimInTerminal(buf) 1119 call delete('XtestBufferScroll') 1120endfunc 1121 1122func Test_pipe_null() 1123 " We cannot check that no I/O works, we only check that the job starts 1124 " properly. 1125 let job = job_start(s:python . " test_channel_pipe.py something", 1126 \ {'in_io': 'null'}) 1127 call assert_equal("run", job_status(job)) 1128 try 1129 call assert_equal('something', ch_read(job)) 1130 finally 1131 call job_stop(job) 1132 endtry 1133 1134 let job = job_start(s:python . " test_channel_pipe.py err-out", 1135 \ {'out_io': 'null'}) 1136 call assert_equal("run", job_status(job)) 1137 try 1138 call assert_equal('err-out', ch_read(job, {"part": "err"})) 1139 finally 1140 call job_stop(job) 1141 endtry 1142 1143 let job = job_start(s:python . " test_channel_pipe.py something", 1144 \ {'err_io': 'null'}) 1145 call assert_equal("run", job_status(job)) 1146 try 1147 call assert_equal('something', ch_read(job)) 1148 finally 1149 call job_stop(job) 1150 endtry 1151 1152 let job = job_start(s:python . " test_channel_pipe.py something", 1153 \ {'out_io': 'null', 'err_io': 'out'}) 1154 call assert_equal("run", job_status(job)) 1155 call job_stop(job) 1156 1157 let job = job_start(s:python . " test_channel_pipe.py something", 1158 \ {'in_io': 'null', 'out_io': 'null', 'err_io': 'null'}) 1159 call assert_equal("run", job_status(job)) 1160 call assert_equal('channel fail', string(job_getchannel(job))) 1161 call assert_equal('fail', ch_status(job)) 1162 call assert_equal('no process', string(test_null_job())) 1163 call assert_equal('channel fail', string(test_null_channel())) 1164 call job_stop(job) 1165endfunc 1166 1167func Test_pipe_to_buffer_raw() 1168 let options = {'out_mode': 'raw', 'out_io': 'buffer', 'out_name': 'testout'} 1169 split testout 1170 let job = job_start([s:python, '-c', 1171 \ 'import sys; [sys.stdout.write(".") and sys.stdout.flush() for _ in range(10000)]'], options) 1172 " the job may be done quickly, also accept "dead" 1173 call assert_match('^\%(dead\|run\)$', job_status(job)) 1174 call WaitFor('len(join(getline(1, "$"), "")) >= 10000') 1175 try 1176 let totlen = 0 1177 for line in getline(1, '$') 1178 call assert_equal('', substitute(line, '^\.*', '', '')) 1179 let totlen += len(line) 1180 endfor 1181 call assert_equal(10000, totlen) 1182 finally 1183 call job_stop(job) 1184 bwipe! 1185 endtry 1186endfunc 1187 1188func Test_reuse_channel() 1189 let job = job_start(s:python . " test_channel_pipe.py") 1190 call assert_equal("run", job_status(job)) 1191 let handle = job_getchannel(job) 1192 try 1193 call ch_sendraw(handle, "echo something\n") 1194 call assert_equal("something", ch_readraw(handle)) 1195 finally 1196 call job_stop(job) 1197 endtry 1198 1199 let job = job_start(s:python . " test_channel_pipe.py", {'channel': handle}) 1200 call assert_equal("run", job_status(job)) 1201 let handle = job_getchannel(job) 1202 try 1203 call ch_sendraw(handle, "echo again\n") 1204 call assert_equal("again", ch_readraw(handle)) 1205 finally 1206 call job_stop(job) 1207 endtry 1208endfunc 1209 1210func Test_out_cb() 1211 let g:test_is_flaky = 1 1212 let dict = {'thisis': 'dict: '} 1213 func dict.outHandler(chan, msg) dict 1214 if type(a:msg) == v:t_string 1215 let g:Ch_outmsg = self.thisis . a:msg 1216 else 1217 let g:Ch_outobj = a:msg 1218 endif 1219 endfunc 1220 func dict.errHandler(chan, msg) dict 1221 let g:Ch_errmsg = self.thisis . a:msg 1222 endfunc 1223 let job = job_start(s:python . " test_channel_pipe.py", 1224 \ {'out_cb': dict.outHandler, 1225 \ 'out_mode': 'json', 1226 \ 'err_cb': dict.errHandler, 1227 \ 'err_mode': 'json'}) 1228 call assert_equal("run", job_status(job)) 1229 call test_garbagecollect_now() 1230 try 1231 let g:Ch_outmsg = '' 1232 let g:Ch_errmsg = '' 1233 call ch_sendraw(job, "echo [0, \"hello\"]\n") 1234 call ch_sendraw(job, "echoerr [0, \"there\"]\n") 1235 call WaitForAssert({-> assert_equal("dict: hello", g:Ch_outmsg)}) 1236 call WaitForAssert({-> assert_equal("dict: there", g:Ch_errmsg)}) 1237 1238 " Receive a json object split in pieces 1239 let g:Ch_outobj = '' 1240 call ch_sendraw(job, "echosplit [0, {\"one\": 1,| \"tw|o\": 2, \"three\": 3|}]\n") 1241 " For unknown reasons this can be very slow on Mac. 1242 if has('mac') 1243 let timeout = 20000 1244 else 1245 let timeout = 5000 1246 endif 1247 call WaitForAssert({-> assert_equal({'one': 1, 'two': 2, 'three': 3}, g:Ch_outobj)}, timeout) 1248 finally 1249 call job_stop(job) 1250 endtry 1251endfunc 1252 1253func Test_out_close_cb() 1254 let s:counter = 1 1255 let g:Ch_msg1 = '' 1256 let g:Ch_closemsg = 0 1257 func! OutHandler(chan, msg) 1258 if s:counter == 1 1259 let g:Ch_msg1 = a:msg 1260 endif 1261 let s:counter += 1 1262 endfunc 1263 func! CloseHandler(chan) 1264 let g:Ch_closemsg = s:counter 1265 let s:counter += 1 1266 endfunc 1267 let job = job_start(s:python . " test_channel_pipe.py quit now", 1268 \ {'out_cb': 'OutHandler', 1269 \ 'close_cb': 'CloseHandler'}) 1270 " the job may be done quickly, also accept "dead" 1271 call assert_match('^\%(dead\|run\)$', job_status(job)) 1272 try 1273 call WaitForAssert({-> assert_equal('quit', g:Ch_msg1)}) 1274 call WaitForAssert({-> assert_equal(2, g:Ch_closemsg)}) 1275 finally 1276 call job_stop(job) 1277 delfunc OutHandler 1278 delfunc CloseHandler 1279 endtry 1280endfunc 1281 1282func Test_read_in_close_cb() 1283 let g:Ch_received = '' 1284 func! CloseHandler(chan) 1285 let g:Ch_received = ch_read(a:chan) 1286 endfunc 1287 let job = job_start(s:python . " test_channel_pipe.py quit now", 1288 \ {'close_cb': 'CloseHandler'}) 1289 " the job may be done quickly, also accept "dead" 1290 call assert_match('^\%(dead\|run\)$', job_status(job)) 1291 try 1292 call WaitForAssert({-> assert_equal('quit', g:Ch_received)}) 1293 finally 1294 call job_stop(job) 1295 delfunc CloseHandler 1296 endtry 1297endfunc 1298 1299" Use channel in NL mode but received text does not end in NL. 1300func Test_read_in_close_cb_incomplete() 1301 let g:Ch_received = '' 1302 func! CloseHandler(chan) 1303 while ch_status(a:chan, {'part': 'out'}) == 'buffered' 1304 let g:Ch_received .= ch_read(a:chan) 1305 endwhile 1306 endfunc 1307 let job = job_start(s:python . " test_channel_pipe.py incomplete", 1308 \ {'close_cb': 'CloseHandler'}) 1309 " the job may be done quickly, also accept "dead" 1310 call assert_match('^\%(dead\|run\)$', job_status(job)) 1311 try 1312 call WaitForAssert({-> assert_equal('incomplete', g:Ch_received)}) 1313 finally 1314 call job_stop(job) 1315 delfunc CloseHandler 1316 endtry 1317endfunc 1318 1319func Test_out_cb_lambda() 1320 let job = job_start(s:python . " test_channel_pipe.py", 1321 \ {'out_cb': {ch, msg -> execute("let g:Ch_outmsg = 'lambda: ' . msg")}, 1322 \ 'out_mode': 'json', 1323 \ 'err_cb': {ch, msg -> execute(":let g:Ch_errmsg = 'lambda: ' . msg")}, 1324 \ 'err_mode': 'json'}) 1325 call assert_equal("run", job_status(job)) 1326 try 1327 let g:Ch_outmsg = '' 1328 let g:Ch_errmsg = '' 1329 call ch_sendraw(job, "echo [0, \"hello\"]\n") 1330 call ch_sendraw(job, "echoerr [0, \"there\"]\n") 1331 call WaitForAssert({-> assert_equal("lambda: hello", g:Ch_outmsg)}) 1332 call WaitForAssert({-> assert_equal("lambda: there", g:Ch_errmsg)}) 1333 finally 1334 call job_stop(job) 1335 endtry 1336endfunc 1337 1338func Test_close_and_exit_cb() 1339 let g:test_is_flaky = 1 1340 let g:retdict = {'ret': {}} 1341 func g:retdict.close_cb(ch) dict 1342 let self.ret['close_cb'] = a:ch->ch_getjob()->job_status() 1343 endfunc 1344 func g:retdict.exit_cb(job, status) dict 1345 let self.ret['exit_cb'] = job_status(a:job) 1346 endfunc 1347 1348 let job = job_start([&shell, &shellcmdflag, 'echo'], 1349 \ {'close_cb': g:retdict.close_cb, 1350 \ 'exit_cb': g:retdict.exit_cb}) 1351 " the job may be done quickly, also accept "dead" 1352 call assert_match('^\%(dead\|run\)$', job_status(job)) 1353 call WaitForAssert({-> assert_equal(2, len(g:retdict.ret))}) 1354 call assert_match('^\%(dead\|run\)$', g:retdict.ret['close_cb']) 1355 call assert_equal('dead', g:retdict.ret['exit_cb']) 1356 unlet g:retdict 1357endfunc 1358 1359"""""""""" 1360 1361function ExitCbWipe(job, status) 1362 exe g:wipe_buf 'bw!' 1363endfunction 1364 1365" This caused a crash, because messages were handled while peeking for a 1366" character. 1367func Test_exit_cb_wipes_buf() 1368 CheckFeature timers 1369 set cursorline lazyredraw 1370 call test_override('redraw_flag', 1) 1371 new 1372 let g:wipe_buf = bufnr('') 1373 1374 let job = job_start(has('win32') ? 'cmd /c echo:' : ['true'], 1375 \ {'exit_cb': 'ExitCbWipe'}) 1376 let timer = timer_start(300, {-> feedkeys("\<Esc>", 'nt')}, {'repeat': 5}) 1377 call feedkeys(repeat('g', 1000) . 'o', 'ntx!') 1378 call WaitForAssert({-> assert_equal("dead", job_status(job))}) 1379 call timer_stop(timer) 1380 1381 set nocursorline nolazyredraw 1382 unlet g:wipe_buf 1383 call test_override('ALL', 0) 1384endfunc 1385 1386"""""""""" 1387 1388let g:Ch_unletResponse = '' 1389func s:UnletHandler(handle, msg) 1390 let g:Ch_unletResponse = a:msg 1391 unlet s:channelfd 1392endfunc 1393 1394" Test that "unlet handle" in a handler doesn't crash Vim. 1395func Ch_unlet_handle(port) 1396 let s:channelfd = ch_open(s:localhost . a:port, s:chopt) 1397 eval s:channelfd->ch_sendexpr("test", {'callback': function('s:UnletHandler')}) 1398 call WaitForAssert({-> assert_equal('what?', g:Ch_unletResponse)}) 1399endfunc 1400 1401func Test_unlet_handle() 1402 call s:run_server('Ch_unlet_handle') 1403endfunc 1404 1405func Test_unlet_handle_ipv6() 1406 CheckIPv6 1407 call Test_unlet_handle() 1408endfunc 1409 1410"""""""""" 1411 1412let g:Ch_unletResponse = '' 1413func Ch_CloseHandler(handle, msg) 1414 let g:Ch_unletResponse = a:msg 1415 eval s:channelfd->ch_close() 1416endfunc 1417 1418" Test that "unlet handle" in a handler doesn't crash Vim. 1419func Ch_close_handle(port) 1420 let s:channelfd = ch_open(s:localhost . a:port, s:chopt) 1421 call ch_sendexpr(s:channelfd, "test", {'callback': function('Ch_CloseHandler')}) 1422 call WaitForAssert({-> assert_equal('what?', g:Ch_unletResponse)}) 1423endfunc 1424 1425func Test_close_handle() 1426 call s:run_server('Ch_close_handle') 1427endfunc 1428 1429func Test_close_handle_ipv6() 1430 CheckIPv6 1431 call Test_close_handle() 1432endfunc 1433 1434"""""""""" 1435 1436func Ch_open_ipv6(port) 1437 let handle = ch_open('[::1]:' .. a:port, s:chopt) 1438 call assert_notequal('fail', ch_status(handle)) 1439endfunc 1440 1441func Test_open_ipv6() 1442 CheckIPv6 1443 call s:run_server('Ch_open_ipv6') 1444endfunc 1445 1446"""""""""" 1447 1448func Test_open_fail() 1449 call assert_fails("let ch = ch_open('noserver')", 'E475:') 1450 echo ch 1451 let d = ch 1452 call assert_fails("let ch = ch_open('noserver', 10)", 'E474:') 1453 call assert_fails("let ch = ch_open('localhost:-1')", 'E475:') 1454 call assert_fails("let ch = ch_open('localhost:65537')", 'E475:') 1455 call assert_fails("let ch = ch_open('localhost:8765', {'timeout' : -1})", 1456 \ 'E474:') 1457 call assert_fails("let ch = ch_open('localhost:8765', {'axby' : 1})", 1458 \ 'E475:') 1459 call assert_fails("let ch = ch_open('localhost:8765', {'mode' : 'abc'})", 1460 \ 'E475:') 1461 call assert_fails("let ch = ch_open('localhost:8765', {'part' : 'out'})", 1462 \ 'E475:') 1463 call assert_fails("let ch = ch_open('[::]')", 'E475:') 1464 call assert_fails("let ch = ch_open('[::.80')", 'E475:') 1465 call assert_fails("let ch = ch_open('[::]8080')", 'E475:') 1466endfunc 1467 1468func Test_ch_info_fail() 1469 call assert_fails("let x = ch_info(10)", 'E475:') 1470endfunc 1471 1472"""""""""" 1473 1474func Ch_open_delay(port) 1475 " Wait up to a second for the port to open. 1476 let s:chopt.waittime = 1000 1477 let channel = ch_open(s:localhost . a:port, s:chopt) 1478 if ch_status(channel) == "fail" 1479 call assert_report("Can't open channel") 1480 return 1481 endif 1482 call assert_equal('got it', channel->ch_evalexpr('hello!')) 1483 call ch_close(channel) 1484endfunc 1485 1486func Test_open_delay() 1487 " The server will wait half a second before creating the port. 1488 call s:run_server('Ch_open_delay', 'delay') 1489endfunc 1490 1491func Test_open_delay_ipv6() 1492 CheckIPv6 1493 call Test_open_delay() 1494endfunc 1495 1496""""""""" 1497 1498function MyFunction(a,b,c) 1499 let g:Ch_call_ret = [a:a, a:b, a:c] 1500endfunc 1501 1502function Ch_test_call(port) 1503 let handle = ch_open(s:localhost . a:port, s:chopt) 1504 if ch_status(handle) == "fail" 1505 call assert_report("Can't open channel") 1506 return 1507 endif 1508 1509 let g:Ch_call_ret = [] 1510 call assert_equal('ok', ch_evalexpr(handle, 'call-func')) 1511 call WaitForAssert({-> assert_equal([1, 2, 3], g:Ch_call_ret)}) 1512 1513 call assert_fails("let i = ch_evalexpr(handle, '2 + 2', {'callback' : 'abc'})", 'E917:') 1514 call assert_fails("let i = ch_evalexpr(handle, '2 + 2', {'drop' : ''})", 'E475:') 1515 call assert_fails("let i = ch_evalexpr(test_null_job(), '2 + 2')", 'E906:') 1516endfunc 1517 1518func Test_call() 1519 call s:run_server('Ch_test_call') 1520endfunc 1521 1522func Test_call_ipv6() 1523 CheckIPv6 1524 call Test_call() 1525endfunc 1526 1527""""""""" 1528 1529let g:Ch_job_exit_ret = 'not yet' 1530function MyExitCb(job, status) 1531 let g:Ch_job_exit_ret = 'done' 1532endfunc 1533 1534function Ch_test_exit_callback(port) 1535 eval g:currentJob->job_setoptions({'exit_cb': 'MyExitCb'}) 1536 let g:Ch_exit_job = g:currentJob 1537 call assert_equal('MyExitCb', job_info(g:currentJob)['exit_cb']) 1538endfunc 1539 1540func Test_exit_callback() 1541 call s:run_server('Ch_test_exit_callback') 1542 1543 " wait up to a second for the job to exit 1544 for i in range(100) 1545 if g:Ch_job_exit_ret == 'done' 1546 break 1547 endif 1548 sleep 10m 1549 " calling job_status() triggers the callback 1550 call job_status(g:Ch_exit_job) 1551 endfor 1552 1553 call assert_equal('done', g:Ch_job_exit_ret) 1554 call assert_equal('dead', job_info(g:Ch_exit_job).status) 1555 unlet g:Ch_exit_job 1556endfunc 1557 1558function MyExitTimeCb(job, status) 1559 if job_info(a:job).process == g:exit_cb_val.process 1560 let g:exit_cb_val.end = reltime(g:exit_cb_val.start) 1561 endif 1562 call Resume() 1563endfunction 1564 1565func Test_exit_callback_interval() 1566 CheckFunction reltimefloat 1567 let g:test_is_flaky = 1 1568 1569 let g:exit_cb_val = {'start': reltime(), 'end': 0, 'process': 0} 1570 let job = [s:python, '-c', 'import time;time.sleep(0.5)']->job_start({'exit_cb': 'MyExitTimeCb'}) 1571 let g:exit_cb_val.process = job_info(job).process 1572 call WaitFor('type(g:exit_cb_val.end) != v:t_number || g:exit_cb_val.end != 0') 1573 let elapsed = reltimefloat(g:exit_cb_val.end) 1574 call assert_true(elapsed > 0.5) 1575 call assert_true(elapsed < 1.0) 1576 1577 " case: unreferenced job, using timer 1578 if !has('timers') 1579 return 1580 endif 1581 1582 let g:exit_cb_val = {'start': reltime(), 'end': 0, 'process': 0} 1583 let g:job = job_start([s:python, '-c', 'import time;time.sleep(0.5)'], {'exit_cb': 'MyExitTimeCb'}) 1584 let g:exit_cb_val.process = job_info(g:job).process 1585 unlet g:job 1586 call Standby(1000) 1587 if type(g:exit_cb_val.end) != v:t_number || g:exit_cb_val.end != 0 1588 let elapsed = reltimefloat(g:exit_cb_val.end) 1589 else 1590 let elapsed = 1.0 1591 endif 1592 call assert_inrange(0.5, 1.0, elapsed) 1593endfunc 1594 1595""""""""" 1596 1597let g:Ch_close_ret = 'alive' 1598function MyCloseCb(ch) 1599 let g:Ch_close_ret = 'closed' 1600endfunc 1601 1602function Ch_test_close_callback(port) 1603 let handle = ch_open(s:localhost . a:port, s:chopt) 1604 if ch_status(handle) == "fail" 1605 call assert_report("Can't open channel") 1606 return 1607 endif 1608 call ch_setoptions(handle, {'close_cb': 'MyCloseCb'}) 1609 1610 call assert_equal('', ch_evalexpr(handle, 'close me')) 1611 call WaitForAssert({-> assert_equal('closed', g:Ch_close_ret)}) 1612endfunc 1613 1614func Test_close_callback() 1615 call s:run_server('Ch_test_close_callback') 1616endfunc 1617 1618func Test_close_callback_ipv6() 1619 CheckIPv6 1620 call Test_close_callback() 1621endfunc 1622 1623function Ch_test_close_partial(port) 1624 let handle = ch_open(s:localhost . a:port, s:chopt) 1625 if ch_status(handle) == "fail" 1626 call assert_report("Can't open channel") 1627 return 1628 endif 1629 let g:Ch_d = {} 1630 func g:Ch_d.closeCb(ch) dict 1631 let self.close_ret = 'closed' 1632 endfunc 1633 call ch_setoptions(handle, {'close_cb': g:Ch_d.closeCb}) 1634 1635 call assert_equal('', ch_evalexpr(handle, 'close me')) 1636 call WaitForAssert({-> assert_equal('closed', g:Ch_d.close_ret)}) 1637 unlet g:Ch_d 1638endfunc 1639 1640func Test_close_partial() 1641 call s:run_server('Ch_test_close_partial') 1642endfunc 1643 1644func Test_close_partial_ipv6() 1645 CheckIPv6 1646 call Test_close_partial() 1647endfunc 1648 1649func Test_job_start_fails() 1650 " this was leaking memory 1651 call assert_fails("call job_start([''])", "E474:") 1652 call assert_fails('call job_start($x)', 'E474:') 1653 call assert_fails('call job_start("")', 'E474:') 1654 call assert_fails('call job_start("ls", {"out_io" : "abc"})', 'E475:') 1655 call assert_fails('call job_start("ls", {"err_io" : "abc"})', 'E475:') 1656 call assert_fails('call job_start("ls", [])', 'E715:') 1657 call assert_fails("call job_start('ls', {'in_top' : -1})", 'E475:') 1658 call assert_fails("call job_start('ls', {'in_bot' : -1})", 'E475:') 1659 call assert_fails("call job_start('ls', {'channel' : -1})", 'E475:') 1660 call assert_fails("call job_start('ls', {'callback' : -1})", 'E921:') 1661 call assert_fails("call job_start('ls', {'out_cb' : -1})", 'E921:') 1662 call assert_fails("call job_start('ls', {'err_cb' : -1})", 'E921:') 1663 call assert_fails("call job_start('ls', {'close_cb' : -1})", 'E921:') 1664 call assert_fails("call job_start('ls', {'exit_cb' : -1})", 'E921:') 1665 call assert_fails("call job_start('ls', {'term_name' : []})", 'E475:') 1666 call assert_fails("call job_start('ls', {'term_finish' : 'run'})", 'E475:') 1667 call assert_fails("call job_start('ls', {'term_api' : []})", 'E475:') 1668 call assert_fails("call job_start('ls', {'stoponexit' : []})", 'E730:') 1669 call assert_fails("call job_start('ls', {'in_io' : 'file'})", 'E920:') 1670 call assert_fails("call job_start('ls', {'out_io' : 'file'})", 'E920:') 1671 call assert_fails("call job_start('ls', {'err_io' : 'file'})", 'E920:') 1672 call assert_fails("call job_start('ls', {'in_mode' : 'abc'})", 'E475:') 1673 call assert_fails("call job_start('ls', {'out_mode' : 'abc'})", 'E475:') 1674 call assert_fails("call job_start('ls', {'err_mode' : 'abc'})", 'E475:') 1675 call assert_fails("call job_start('ls', 1676 \ {'in_io' : 'buffer', 'in_buf' : 99999})", 'E86:') 1677 call assert_fails("call job_start('ls', 1678 \ {'out_io' : 'buffer', 'out_buf' : 99999})", 'E86:') 1679 call assert_fails("call job_start('ls', 1680 \ {'err_io' : 'buffer', 'err_buf' : 99999})", 'E86:') 1681 1682 call assert_fails("call job_start('ls', 1683 \ {'in_io' : 'buffer', 'in_buf' : -1})", 'E475:') 1684 call assert_fails("call job_start('ls', 1685 \ {'out_io' : 'buffer', 'out_buf' : -1})", 'E475:') 1686 call assert_fails("call job_start('ls', 1687 \ {'err_io' : 'buffer', 'err_buf' : -1})", 'E475:') 1688 1689 set nomodifiable 1690 call assert_fails("call job_start('cmd /c dir', 1691 \ {'out_io' : 'buffer', 'out_buf' :" .. bufnr() .. "})", 'E21:') 1692 call assert_fails("call job_start('cmd /c dir', 1693 \ {'err_io' : 'buffer', 'err_buf' :" .. bufnr() .. "})", 'E21:') 1694 set modifiable 1695 1696 call assert_fails("call job_start('ls', {'in_io' : 'buffer'})", 'E915:') 1697 1698 edit! XXX 1699 let bnum = bufnr() 1700 enew 1701 call assert_fails("call job_start('ls', 1702 \ {'in_io' : 'buffer', 'in_buf' : bnum})", 'E918:') 1703 1704 " Empty job tests 1705 " This was crashing on MS-Windows. 1706 call assert_fails('let job = job_start([""])', 'E474:') 1707 call assert_fails('let job = job_start([" "])', 'E474:') 1708 call assert_fails('let job = job_start("")', 'E474:') 1709 call assert_fails('let job = job_start(" ")', 'E474:') 1710 call assert_fails('let job = job_start(["ls", []])', 'E730:') 1711 call assert_fails('call job_setoptions(test_null_job(), {})', 'E916:') 1712 %bw! 1713endfunc 1714 1715func Test_job_stop_immediately() 1716 let g:job = job_start([s:python, '-c', 'import time;time.sleep(10)']) 1717 try 1718 eval g:job->job_stop() 1719 call WaitForAssert({-> assert_equal('dead', job_status(g:job))}) 1720 finally 1721 call job_stop(g:job, 'kill') 1722 unlet g:job 1723 endtry 1724endfunc 1725 1726func Test_null_job_eval() 1727 call assert_fails('eval test_null_job()->eval()', 'E121:') 1728endfunc 1729 1730" This was leaking memory. 1731func Test_partial_in_channel_cycle() 1732 let d = {} 1733 let d.a = function('string', [d]) 1734 try 1735 let d.b = ch_open('nowhere:123', {'close_cb': d.a}) 1736 call test_garbagecollect_now() 1737 catch 1738 call assert_exception('E901:') 1739 endtry 1740 unlet d 1741endfunc 1742 1743func Test_using_freed_memory() 1744 let g:a = job_start(['ls']) 1745 sleep 10m 1746 call test_garbagecollect_now() 1747endfunc 1748 1749func Test_collapse_buffers() 1750 let g:test_is_flaky = 1 1751 CheckExecutable cat 1752 1753 sp test_channel.vim 1754 let g:linecount = line('$') 1755 close 1756 split testout 1757 1,$delete 1758 call job_start('cat test_channel.vim', {'out_io': 'buffer', 'out_name': 'testout'}) 1759 call WaitForAssert({-> assert_inrange(g:linecount, g:linecount + 1, line('$'))}) 1760 bwipe! 1761endfunc 1762 1763func Test_write_to_deleted_buffer() 1764 CheckExecutable echo 1765 CheckFeature quickfix 1766 1767 let job = job_start('echo hello', {'out_io': 'buffer', 'out_name': 'test_buffer', 'out_msg': 0}) 1768 let bufnr = bufnr('test_buffer') 1769 call WaitForAssert({-> assert_equal(['hello'], getbufline(bufnr, 1, '$'))}) 1770 call assert_equal('nofile', getbufvar(bufnr, '&buftype')) 1771 call assert_equal('hide', getbufvar(bufnr, '&bufhidden')) 1772 1773 bdel test_buffer 1774 call assert_equal([], getbufline(bufnr, 1, '$')) 1775 1776 let job = job_start('echo hello', {'out_io': 'buffer', 'out_name': 'test_buffer', 'out_msg': 0}) 1777 call WaitForAssert({-> assert_equal(['hello'], getbufline(bufnr, 1, '$'))}) 1778 call assert_equal('nofile', getbufvar(bufnr, '&buftype')) 1779 call assert_equal('hide', getbufvar(bufnr, '&bufhidden')) 1780 1781 bwipe! test_buffer 1782endfunc 1783 1784func Test_cmd_parsing() 1785 CheckUnix 1786 1787 call assert_false(filereadable("file with space")) 1788 let job = job_start('touch "file with space"') 1789 call WaitForAssert({-> assert_true(filereadable("file with space"))}) 1790 call delete("file with space") 1791 1792 let job = job_start('touch file\ with\ space') 1793 call WaitForAssert({-> assert_true(filereadable("file with space"))}) 1794 call delete("file with space") 1795endfunc 1796 1797func Test_raw_passes_nul() 1798 CheckExecutable cat 1799 1800 " Test lines from the job containing NUL are stored correctly in a buffer. 1801 new 1802 call setline(1, ["asdf\nasdf", "xxx\n", "\nyyy"]) 1803 w! Xtestread 1804 bwipe! 1805 split testout 1806 1,$delete 1807 call job_start('cat Xtestread', {'out_io': 'buffer', 'out_name': 'testout'}) 1808 call WaitFor('line("$") > 2') 1809 call assert_equal("asdf\nasdf", getline(1)) 1810 call assert_equal("xxx\n", getline(2)) 1811 call assert_equal("\nyyy", getline(3)) 1812 1813 call delete('Xtestread') 1814 bwipe! 1815 1816 " Test lines from a buffer with NUL bytes are written correctly to the job. 1817 new mybuffer 1818 call setline(1, ["asdf\nasdf", "xxx\n", "\nyyy"]) 1819 let g:Ch_job = job_start('cat', {'in_io': 'buffer', 'in_name': 'mybuffer', 'out_io': 'file', 'out_name': 'Xtestwrite'}) 1820 call WaitForAssert({-> assert_equal("dead", job_status(g:Ch_job))}) 1821 bwipe! 1822 split Xtestwrite 1823 call assert_equal("asdf\nasdf", getline(1)) 1824 call assert_equal("xxx\n", getline(2)) 1825 call assert_equal("\nyyy", getline(3)) 1826 call assert_equal(-1, match(s:get_resources(), '\(^\|/\)Xtestwrite$')) 1827 1828 call delete('Xtestwrite') 1829 bwipe! 1830endfunc 1831 1832func Test_read_nonl_line() 1833 let g:linecount = 0 1834 let arg = 'import sys;sys.stdout.write("1\n2\n3")' 1835 call job_start([s:python, '-c', arg], {'callback': {-> execute('let g:linecount += 1')}}) 1836 call WaitForAssert({-> assert_equal(3, g:linecount)}) 1837 unlet g:linecount 1838endfunc 1839 1840func Test_read_nonl_in_close_cb() 1841 func s:close_cb(ch) 1842 while ch_status(a:ch) == 'buffered' 1843 let g:out .= ch_read(a:ch) 1844 endwhile 1845 endfunc 1846 1847 let g:out = '' 1848 let arg = 'import sys;sys.stdout.write("1\n2\n3")' 1849 call job_start([s:python, '-c', arg], {'close_cb': function('s:close_cb')}) 1850 call test_garbagecollect_now() 1851 call WaitForAssert({-> assert_equal('123', g:out)}) 1852 unlet g:out 1853 delfunc s:close_cb 1854endfunc 1855 1856func Test_read_from_terminated_job() 1857 let g:linecount = 0 1858 let arg = 'import os,sys;os.close(1);sys.stderr.write("test\n")' 1859 call job_start([s:python, '-c', arg], {'callback': {-> execute('let g:linecount += 1')}}) 1860 call WaitForAssert({-> assert_equal(1, g:linecount)}) 1861 call test_garbagecollect_now() 1862 unlet g:linecount 1863endfunc 1864 1865func Test_job_start_windows() 1866 CheckMSWindows 1867 1868 " Check that backslash in $COMSPEC is handled properly. 1869 let g:echostr = '' 1870 let cmd = $COMSPEC . ' /c echo 123' 1871 let job = job_start(cmd, {'callback': {ch,msg -> execute(":let g:echostr .= msg")}}) 1872 let info = job_info(job) 1873 call assert_equal([$COMSPEC, '/c', 'echo', '123'], info.cmd) 1874 1875 call WaitForAssert({-> assert_equal("123", g:echostr)}) 1876 unlet g:echostr 1877endfunc 1878 1879func Test_env() 1880 let g:envstr = '' 1881 if has('win32') 1882 let cmd = ['cmd', '/c', 'echo %FOO%'] 1883 else 1884 let cmd = [&shell, &shellcmdflag, 'echo $FOO'] 1885 endif 1886 call assert_fails('call job_start(cmd, {"env": 1})', 'E475:') 1887 call job_start(cmd, {'callback': {ch,msg -> execute(":let g:envstr .= msg")}, 'env': {'FOO': 'bar'}}) 1888 call WaitForAssert({-> assert_equal("bar", g:envstr)}) 1889 unlet g:envstr 1890endfunc 1891 1892func Test_cwd() 1893 let g:test_is_flaky = 1 1894 let g:envstr = '' 1895 if has('win32') 1896 let expect = $TEMP 1897 let cmd = ['cmd', '/c', 'echo %CD%'] 1898 else 1899 let expect = $HOME 1900 let cmd = ['pwd'] 1901 endif 1902 let job = job_start(cmd, {'callback': {ch,msg -> execute(":let g:envstr .= msg")}, 'cwd': expect}) 1903 try 1904 call WaitForAssert({-> assert_notequal("", g:envstr)}) 1905 let expect = substitute(expect, '[/\\]$', '', '') 1906 let g:envstr = substitute(g:envstr, '[/\\]$', '', '') 1907 if $CI != '' && stridx(g:envstr, '/private/') == 0 1908 let g:envstr = g:envstr[8:] 1909 endif 1910 call assert_equal(expect, g:envstr) 1911 finally 1912 call job_stop(job) 1913 unlet g:envstr 1914 endtry 1915endfunc 1916 1917function Ch_test_close_lambda(port) 1918 let handle = ch_open(s:localhost . a:port, s:chopt) 1919 if ch_status(handle) == "fail" 1920 call assert_report("Can't open channel") 1921 return 1922 endif 1923 let g:Ch_close_ret = '' 1924 call ch_setoptions(handle, {'close_cb': {ch -> execute("let g:Ch_close_ret = 'closed'")}}) 1925 call test_garbagecollect_now() 1926 1927 call assert_equal('', ch_evalexpr(handle, 'close me')) 1928 call WaitForAssert({-> assert_equal('closed', g:Ch_close_ret)}) 1929endfunc 1930 1931func Test_close_lambda() 1932 call s:run_server('Ch_test_close_lambda') 1933endfunc 1934 1935func Test_close_lambda_ipv6() 1936 CheckIPv6 1937 call Test_close_lambda() 1938endfunc 1939 1940func s:test_list_args(cmd, out, remove_lf) 1941 try 1942 let g:out = '' 1943 let job = job_start([s:python, '-c', a:cmd], {'callback': {ch, msg -> execute('let g:out .= msg')}, 'out_mode': 'raw'}) 1944 call WaitFor('"" != g:out') 1945 if has('win32') 1946 let g:out = substitute(g:out, '\r', '', 'g') 1947 endif 1948 if a:remove_lf 1949 let g:out = substitute(g:out, '\n$', '', 'g') 1950 endif 1951 call assert_equal(a:out, g:out) 1952 finally 1953 call job_stop(job) 1954 unlet g:out 1955 endtry 1956endfunc 1957 1958func Test_list_args() 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\nworld")', "hello\nworld", 0) 1961 call s:test_list_args('import sys;sys.stdout.write(''hello\nworld'')', "hello\nworld", 0) 1962 call s:test_list_args('import sys;sys.stdout.write(''hello"world'')', "hello\"world", 0) 1963 call s:test_list_args('import sys;sys.stdout.write(''hello^world'')', "hello^world", 0) 1964 call s:test_list_args('import sys;sys.stdout.write("hello&&world")', "hello&&world", 0) 1965 call s:test_list_args('import sys;sys.stdout.write(''hello\\world'')', "hello\\world", 0) 1966 call s:test_list_args('import sys;sys.stdout.write(''hello\\\\world'')', "hello\\\\world", 0) 1967 call s:test_list_args('import sys;sys.stdout.write("hello\"world\"")', 'hello"world"', 0) 1968 call s:test_list_args('import sys;sys.stdout.write("h\"ello worl\"d")', 'h"ello worl"d', 0) 1969 call s:test_list_args('import sys;sys.stdout.write("h\"e\\\"llo wor\\\"l\"d")', 'h"e\"llo wor\"l"d', 0) 1970 call s:test_list_args('import sys;sys.stdout.write("h\"e\\\"llo world")', 'h"e\"llo world', 0) 1971 call s:test_list_args('import sys;sys.stdout.write("hello\tworld")', "hello\tworld", 0) 1972 1973 " tests which not contain spaces in the argument 1974 call s:test_list_args('print("hello\nworld")', "hello\nworld", 1) 1975 call s:test_list_args('print(''hello\nworld'')', "hello\nworld", 1) 1976 call s:test_list_args('print(''hello"world'')', "hello\"world", 1) 1977 call s:test_list_args('print(''hello^world'')', "hello^world", 1) 1978 call s:test_list_args('print("hello&&world")', "hello&&world", 1) 1979 call s:test_list_args('print(''hello\\world'')', "hello\\world", 1) 1980 call s:test_list_args('print(''hello\\\\world'')', "hello\\\\world", 1) 1981 call s:test_list_args('print("hello\"world\"")', 'hello"world"', 1) 1982 call s:test_list_args('print("hello\tworld")', "hello\tworld", 1) 1983endfunc 1984 1985func Test_keep_pty_open() 1986 CheckUnix 1987 1988 let job = job_start(s:python . ' -c "import time;time.sleep(0.2)"', 1989 \ {'out_io': 'null', 'err_io': 'null', 'pty': 1}) 1990 let elapsed = WaitFor({-> job_status(job) ==# 'dead'}) 1991 call assert_inrange(200, 1000, elapsed) 1992 call job_stop(job) 1993endfunc 1994 1995func Test_job_start_in_timer() 1996 CheckFeature timers 1997 CheckFunction reltimefloat 1998 1999 func OutCb(chan, msg) 2000 let g:val += 1 2001 endfunc 2002 2003 func ExitCb(job, status) 2004 let g:val += 1 2005 call Resume() 2006 endfunc 2007 2008 func TimerCb(timer) 2009 if has('win32') 2010 let cmd = ['cmd', '/c', 'echo.'] 2011 else 2012 let cmd = ['echo'] 2013 endif 2014 let g:job = job_start(cmd, {'out_cb': 'OutCb', 'exit_cb': 'ExitCb'}) 2015 call substitute(repeat('a', 100000), '.', '', 'g') 2016 endfunc 2017 2018 " We should be interrupted before 'updatetime' elapsed. 2019 let g:val = 0 2020 call timer_start(1, 'TimerCb') 2021 let elapsed = Standby(&ut) 2022 call assert_inrange(1, &ut / 2, elapsed) 2023 2024 " Wait for both OutCb() and ExitCb() to have been called before deleting 2025 " them. 2026 call WaitForAssert({-> assert_equal(2, g:val)}) 2027 call job_stop(g:job) 2028 2029 delfunc OutCb 2030 delfunc ExitCb 2031 delfunc TimerCb 2032 unlet! g:val 2033 unlet! g:job 2034endfunc 2035 2036func Test_raw_large_data() 2037 try 2038 let g:out = '' 2039 let job = job_start(s:python . " test_channel_pipe.py", 2040 \ {'mode': 'raw', 'drop': 'never', 'noblock': 1, 2041 \ 'callback': {ch, msg -> execute('let g:out .= msg')}}) 2042 2043 let outlen = 79999 2044 let want = repeat('X', outlen) . "\n" 2045 eval job->ch_sendraw(want) 2046 call WaitFor({-> len(g:out) >= outlen}, 10000) 2047 call WaitForAssert({-> assert_equal("dead", job_status(job))}) 2048 call assert_equal(want, substitute(g:out, '\r', '', 'g')) 2049 finally 2050 call job_stop(job) 2051 unlet g:out 2052 endtry 2053endfunc 2054 2055func Test_no_hang_windows() 2056 CheckMSWindows 2057 2058 try 2059 let job = job_start(s:python . " test_channel_pipe.py busy", 2060 \ {'mode': 'raw', 'drop': 'never', 'noblock': 0}) 2061 call assert_fails('call ch_sendraw(job, repeat("X", 80000))', 'E631:') 2062 finally 2063 call job_stop(job) 2064 endtry 2065endfunc 2066 2067func Test_job_exitval_and_termsig() 2068 CheckUnix 2069 2070 " Terminate job normally 2071 let cmd = ['echo'] 2072 let job = job_start(cmd) 2073 call WaitForAssert({-> assert_equal("dead", job_status(job))}) 2074 let info = job_info(job) 2075 call assert_equal(0, info.exitval) 2076 call assert_equal("", info.termsig) 2077 2078 " Terminate job by signal 2079 let cmd = ['sleep', '10'] 2080 let job = job_start(cmd) 2081 " 10m usually works but 50m is needed when running Valgrind 2082 sleep 50m 2083 call job_stop(job) 2084 call WaitForAssert({-> assert_equal("dead", job_status(job))}) 2085 let info = job_info(job) 2086 call assert_equal(-1, info.exitval) 2087 call assert_equal("term", info.termsig) 2088endfunc 2089 2090func Test_job_tty_in_out() 2091 CheckUnix 2092 2093 call writefile(['test'], 'Xtestin') 2094 let in_opts = [{}, 2095 \ {'in_io': 'null'}, 2096 \ {'in_io': 'file', 'in_name': 'Xtestin'}] 2097 let out_opts = [{}, 2098 \ {'out_io': 'null'}, 2099 \ {'out_io': 'file', 'out_name': 'Xtestout'}] 2100 let err_opts = [{}, 2101 \ {'err_io': 'null'}, 2102 \ {'err_io': 'file', 'err_name': 'Xtesterr'}, 2103 \ {'err_io': 'out'}] 2104 let opts = [] 2105 2106 for in_opt in in_opts 2107 let x = copy(in_opt) 2108 for out_opt in out_opts 2109 let x = extend(copy(x), out_opt) 2110 for err_opt in err_opts 2111 let x = extend(copy(x), err_opt) 2112 let opts += [extend({'pty': 1}, x)] 2113 endfor 2114 endfor 2115 endfor 2116 2117 for opt in opts 2118 let job = job_start('echo', opt) 2119 let info = job_info(job) 2120 let msg = printf('option={"in_io": "%s", "out_io": "%s", "err_io": "%s"}', 2121 \ get(opt, 'in_io', 'tty'), 2122 \ get(opt, 'out_io', 'tty'), 2123 \ get(opt, 'err_io', 'tty')) 2124 2125 if !has_key(opt, 'in_io') || !has_key(opt, 'out_io') || !has_key(opt, 'err_io') 2126 call assert_notequal('', info.tty_in, msg) 2127 else 2128 call assert_equal('', info.tty_in, msg) 2129 endif 2130 call assert_equal(info.tty_in, info.tty_out, msg) 2131 2132 call WaitForAssert({-> assert_equal('dead', job_status(job))}) 2133 endfor 2134 2135 call delete('Xtestin') 2136 call delete('Xtestout') 2137 call delete('Xtesterr') 2138endfunc 2139 2140" Do this last, it stops any channel log. 2141func Test_zz_nl_err_to_out_pipe() 2142 2143 eval 'Xlog'->ch_logfile() 2144 call ch_log('Test_zz_nl_err_to_out_pipe()') 2145 let job = job_start(s:python . " test_channel_pipe.py", {'err_io': 'out'}) 2146 call assert_equal("run", job_status(job)) 2147 try 2148 let handle = job_getchannel(job) 2149 call ch_sendraw(handle, "echo something\n") 2150 call assert_equal("something", ch_readraw(handle)) 2151 2152 call ch_sendraw(handle, "echoerr wrong\n") 2153 call assert_equal("wrong", ch_readraw(handle)) 2154 finally 2155 call job_stop(job) 2156 call ch_logfile('') 2157 let loglines = readfile('Xlog') 2158 call assert_true(len(loglines) > 10) 2159 let found_test = 0 2160 let found_send = 0 2161 let found_recv = 0 2162 let found_stop = 0 2163 for l in loglines 2164 if l =~ 'Test_zz_nl_err_to_out_pipe' 2165 let found_test = 1 2166 endif 2167 if l =~ 'SEND on.*echo something' 2168 let found_send = 1 2169 endif 2170 if l =~ 'RECV on.*something' 2171 let found_recv = 1 2172 endif 2173 if l =~ 'Stopping job with' 2174 let found_stop = 1 2175 endif 2176 endfor 2177 call assert_equal(1, found_test) 2178 call assert_equal(1, found_send) 2179 call assert_equal(1, found_recv) 2180 call assert_equal(1, found_stop) 2181 " On MS-Windows need to sleep for a moment to be able to delete the file. 2182 sleep 10m 2183 call delete('Xlog') 2184 endtry 2185endfunc 2186 2187" Do this last, it stops any channel log. 2188func Test_zz_ch_log() 2189 call ch_logfile('Xlog', 'w') 2190 call ch_log('hello there') 2191 call ch_log('%s%s') 2192 call ch_logfile('') 2193 let text = readfile('Xlog') 2194 call assert_match("hello there", text[1]) 2195 call assert_match("%s%s", text[2]) 2196 call mkdir("Xdir1") 2197 call assert_fails("call ch_logfile('Xdir1')", 'E484:') 2198 cal delete("Xdir1", 'd') 2199 call delete('Xlog') 2200endfunc 2201 2202func Test_issue_5150() 2203 if has('win32') 2204 let cmd = 'cmd /c pause' 2205 else 2206 let cmd = 'grep foo' 2207 endif 2208 2209 let g:job = job_start(cmd, {}) 2210 sleep 50m " give the job time to start 2211 call job_stop(g:job) 2212 call WaitForAssert({-> assert_equal(-1, job_info(g:job).exitval)}) 2213 2214 let g:job = job_start(cmd, {}) 2215 sleep 50m 2216 call job_stop(g:job, 'term') 2217 call WaitForAssert({-> assert_equal(-1, job_info(g:job).exitval)}) 2218 2219 let g:job = job_start(cmd, {}) 2220 sleep 50m 2221 call job_stop(g:job, 'kill') 2222 call WaitForAssert({-> assert_equal(-1, job_info(g:job).exitval)}) 2223endfunc 2224 2225func Test_issue_5485() 2226 let $VAR1 = 'global' 2227 let g:Ch_reply = "" 2228 let l:job = job_start([&shell, &shellcmdflag, has('win32') ? 'echo %VAR1% %VAR2%' : 'echo $VAR1 $VAR2'], {'env': {'VAR1': 'local', 'VAR2': 'local'}, 'callback': 'Ch_handler'}) 2229 let g:Ch_job = l:job 2230 call WaitForAssert({-> assert_equal("local local", trim(g:Ch_reply))}) 2231 unlet $VAR1 2232endfunc 2233 2234func Test_job_trailing_space_unix() 2235 CheckUnix 2236 CheckExecutable cat 2237 2238 let job = job_start("cat ", #{in_io: 'null'}) 2239 call WaitForAssert({-> assert_equal("dead", job_status(job))}) 2240 call assert_equal(0, job_info(job).exitval) 2241endfunc 2242 2243func Test_ch_getbufnr() 2244 let ch = test_null_channel() 2245 call assert_equal(-1, ch_getbufnr(ch, 'in')) 2246 call assert_equal(-1, ch_getbufnr(ch, 'out')) 2247 call assert_equal(-1, ch_getbufnr(ch, 'err')) 2248 call assert_equal(-1, ch_getbufnr(ch, '')) 2249endfunc 2250 2251" Test for unsupported options passed to ch_status() 2252func Test_invalid_job_chan_options() 2253 let ch = test_null_channel() 2254 let invalid_opts = [ 2255 \ {'in_io' : 'null'}, 2256 \ {'out_io' : 'null'}, 2257 \ {'err_io' : 'null'}, 2258 \ {'mode' : 'json'}, 2259 \ {'out_mode' : 'json'}, 2260 \ {'err_mode' : 'json'}, 2261 \ {'noblock' : 1}, 2262 \ {'in_name' : '/a/b'}, 2263 \ {'pty' : 1}, 2264 \ {'in_buf' : 1}, 2265 \ {'out_buf' : 1}, 2266 \ {'err_buf' : 1}, 2267 \ {'out_modifiable' : 1}, 2268 \ {'err_modifiable' : 1}, 2269 \ {'out_msg' : 1}, 2270 \ {'err_msg' : 1}, 2271 \ {'in_top' : 1}, 2272 \ {'in_bot' : 1}, 2273 \ {'channel' : ch}, 2274 \ {'callback' : ''}, 2275 \ {'out_cb' : ''}, 2276 \ {'err_cb' : ''}, 2277 \ {'close_cb' : ''}, 2278 \ {'exit_cb' : ''}, 2279 \ {'term_opencmd' : ''}, 2280 \ {'eof_chars' : ''}, 2281 \ {'term_rows' : 10}, 2282 \ {'term_cols' : 10}, 2283 \ {'vertical' : 0}, 2284 \ {'curwin' : 1}, 2285 \ {'bufnr' : 1}, 2286 \ {'hidden' : 0}, 2287 \ {'norestore' : 0}, 2288 \ {'term_kill' : 'kill'}, 2289 \ {'tty_type' : ''}, 2290 \ {'term_highlight' : ''}, 2291 \ {'env' : {}}, 2292 \ {'cwd' : ''}, 2293 \ {'timeout' : 0}, 2294 \ {'out_timeout' : 0}, 2295 \ {'err_timeout' : 0}, 2296 \ {'id' : 0}, 2297 \ {'stoponexit' : ''}, 2298 \ {'block_write' : 1} 2299 \ ] 2300 if has('gui') 2301 call add(invalid_opts, {'ansi_colors' : []}) 2302 endif 2303 2304 for opt in invalid_opts 2305 call assert_fails("let x = ch_status(ch, opt)", 'E475:') 2306 endfor 2307 call assert_equal('fail', ch_status(ch, test_null_dict())) 2308endfunc 2309 2310" Test for passing the command and the arguments as List on MS-Windows 2311func Test_job_with_list_args() 2312 CheckMSWindows 2313 2314 enew! 2315 let bnum = bufnr() 2316 let job = job_start(['cmd', '/c', 'echo', 'Hello', 'World'], {'out_io' : 'buffer', 'out_buf' : bnum}) 2317 call WaitForAssert({-> assert_equal("dead", job_status(job))}) 2318 call assert_equal('Hello World', getline(1)) 2319 %bw! 2320endfunc 2321 2322func ExitCb_cb_with_input(job, status) 2323 call feedkeys(":\<C-u>echo input('', 'default')\<CR>\<CR>", 'nx') 2324 call assert_equal('default', Screenline(&lines)) 2325 let g:wait_exit_cb = 0 2326endfunc 2327 2328func Test_cb_with_input() 2329 let g:wait_exit_cb = 1 2330 2331 if has('win32') 2332 let cmd = 'cmd /c echo "Vim''s test"' 2333 else 2334 let cmd = 'echo "Vim''s test"' 2335 endif 2336 2337 let job = job_start(cmd, {'out_cb': 'ExitCb_cb_with_input'}) 2338 call WaitFor({-> job_status(job) == "dead"}) 2339 call WaitForAssert({-> assert_equal(0, g:wait_exit_cb)}) 2340 2341 unlet g:wait_exit_cb 2342endfunc 2343 2344function s:HandleBufEnter() abort 2345 let queue = [] 2346 let job = job_start(['date'], {'callback': { j, d -> add(queue, d) }}) 2347 while empty(queue) 2348 sleep! 10m 2349 endwhile 2350endfunction 2351 2352func Test_parse_messages_in_autocmd() 2353 CheckUnix 2354 2355 " Check that in the BufEnter autocommand events are being handled 2356 augroup bufenterjob 2357 autocmd! 2358 autocmd BufEnter Xbufenterjob call s:HandleBufEnter() 2359 augroup END 2360 2361 only 2362 split Xbufenterjob 2363 wincmd p 2364 redraw 2365 2366 close 2367 augroup bufenterjob 2368 autocmd! 2369 augroup END 2370endfunc 2371 2372func Test_job_start_with_invalid_argument() 2373 call assert_fails('call job_start([0zff])', 'E976:') 2374endfunc 2375 2376 2377" vim: shiftwidth=2 sts=2 expandtab 2378