1" Test for channel functions. 2 3if !has('channel') 4 finish 5endif 6 7source shared.vim 8 9let s:python = PythonProg() 10if s:python == '' 11 " Can't run this test without Python. 12 finish 13endif 14 15" Uncomment the next line to see what happens. Output is in 16" src/testdir/channellog. 17" call ch_logfile('channellog', 'w') 18 19let s:chopt = {} 20 21" Run "testfunc" after sarting the server and stop the server afterwards. 22func s:run_server(testfunc, ...) 23 call RunServer('test_channel.py', a:testfunc, a:000) 24endfunc 25 26let g:Ch_responseMsg = '' 27func Ch_requestHandler(handle, msg) 28 let g:Ch_responseHandle = a:handle 29 let g:Ch_responseMsg = a:msg 30endfunc 31 32func Ch_communicate(port) 33 " Avoid dropping messages, since we don't use a callback here. 34 let s:chopt.drop = 'never' 35 let handle = ch_open('localhost:' . a:port, s:chopt) 36 unlet s:chopt.drop 37 if ch_status(handle) == "fail" 38 call assert_report("Can't open channel") 39 return 40 endif 41 if has('job') 42 " check that getjob without a job is handled correctly 43 call assert_equal('no process', string(ch_getjob(handle))) 44 endif 45 let dict = ch_info(handle) 46 call assert_true(dict.id != 0) 47 call assert_equal('open', dict.status) 48 call assert_equal(a:port, string(dict.port)) 49 call assert_equal('open', dict.sock_status) 50 call assert_equal('socket', dict.sock_io) 51 52 " Simple string request and reply. 53 call assert_equal('got it', ch_evalexpr(handle, 'hello!')) 54 55 " Malformed command should be ignored. 56 call assert_equal('ok', ch_evalexpr(handle, 'malformed1')) 57 call assert_equal('ok', ch_evalexpr(handle, 'malformed2')) 58 call assert_equal('ok', ch_evalexpr(handle, 'malformed3')) 59 60 " split command should work 61 call assert_equal('ok', ch_evalexpr(handle, 'split')) 62 call WaitFor('exists("g:split")') 63 call assert_equal(123, g:split) 64 65 " string with ][ should work 66 call assert_equal('this][that', ch_evalexpr(handle, 'echo this][that')) 67 68 " nothing to read now 69 call assert_equal(0, ch_canread(handle)) 70 71 " sending three messages quickly then reading should work 72 for i in range(3) 73 call ch_sendexpr(handle, 'echo hello ' . i) 74 endfor 75 call assert_equal('hello 0', ch_read(handle)[1]) 76 call assert_equal('hello 1', ch_read(handle)[1]) 77 call assert_equal('hello 2', ch_read(handle)[1]) 78 79 " Request that triggers sending two ex commands. These will usually be 80 " handled before getting the response, but it's not guaranteed, thus wait a 81 " tiny bit for the commands to get executed. 82 call assert_equal('ok', ch_evalexpr(handle, 'make change')) 83 call WaitFor('"added2" == getline("$")') 84 call assert_equal('added1', getline(line('$') - 1)) 85 call assert_equal('added2', getline('$')) 86 87 " Request command "foo bar", which fails silently. 88 call assert_equal('ok', ch_evalexpr(handle, 'bad command')) 89 call WaitFor('v:errmsg =~ "E492"') 90 call assert_match('E492:.*foo bar', v:errmsg) 91 92 call assert_equal('ok', ch_evalexpr(handle, 'do normal', {'timeout': 100})) 93 call WaitFor('"added more" == getline("$")') 94 call assert_equal('added more', getline('$')) 95 96 " Send a request with a specific handler. 97 call ch_sendexpr(handle, 'hello!', {'callback': 'Ch_requestHandler'}) 98 call WaitFor('exists("g:Ch_responseHandle")') 99 if !exists('g:Ch_responseHandle') 100 call assert_report('g:Ch_responseHandle was not set') 101 else 102 call assert_equal(handle, g:Ch_responseHandle) 103 unlet g:Ch_responseHandle 104 endif 105 call assert_equal('got it', g:Ch_responseMsg) 106 107 let g:Ch_responseMsg = '' 108 call ch_sendexpr(handle, 'hello!', {'callback': function('Ch_requestHandler')}) 109 call WaitFor('exists("g:Ch_responseHandle")') 110 if !exists('g:Ch_responseHandle') 111 call assert_report('g:Ch_responseHandle was not set') 112 else 113 call assert_equal(handle, g:Ch_responseHandle) 114 unlet g:Ch_responseHandle 115 endif 116 call assert_equal('got it', g:Ch_responseMsg) 117 118 " Using lambda. 119 let g:Ch_responseMsg = '' 120 call ch_sendexpr(handle, 'hello!', {'callback': {a, b -> Ch_requestHandler(a, b)}}) 121 call WaitFor('exists("g:Ch_responseHandle")') 122 if !exists('g:Ch_responseHandle') 123 call assert_report('g:Ch_responseHandle was not set') 124 else 125 call assert_equal(handle, g:Ch_responseHandle) 126 unlet g:Ch_responseHandle 127 endif 128 call assert_equal('got it', g:Ch_responseMsg) 129 130 " Collect garbage, tests that our handle isn't collected. 131 call test_garbagecollect_now() 132 133 " check setting options (without testing the effect) 134 call ch_setoptions(handle, {'callback': 's:NotUsed'}) 135 call ch_setoptions(handle, {'timeout': 1111}) 136 call ch_setoptions(handle, {'mode': 'json'}) 137 call assert_fails("call ch_setoptions(handle, {'waittime': 111})", "E475") 138 call ch_setoptions(handle, {'callback': ''}) 139 call ch_setoptions(handle, {'drop': 'never'}) 140 call ch_setoptions(handle, {'drop': 'auto'}) 141 call assert_fails("call ch_setoptions(handle, {'drop': 'bad'})", "E475") 142 143 " Send an eval request that works. 144 call assert_equal('ok', ch_evalexpr(handle, 'eval-works')) 145 sleep 10m 146 call assert_equal([-1, 'foo123'], ch_evalexpr(handle, 'eval-result')) 147 148 " Send an eval request with special characters. 149 call assert_equal('ok', ch_evalexpr(handle, 'eval-special')) 150 sleep 10m 151 call assert_equal([-2, "foo\x7f\x10\x01bar"], ch_evalexpr(handle, 'eval-result')) 152 153 " Send an eval request to get a line with special characters. 154 call setline(3, "a\nb\<CR>c\x01d\x7fe") 155 call assert_equal('ok', ch_evalexpr(handle, 'eval-getline')) 156 sleep 10m 157 call assert_equal([-3, "a\nb\<CR>c\x01d\x7fe"], ch_evalexpr(handle, 'eval-result')) 158 159 " Send an eval request that fails. 160 call assert_equal('ok', ch_evalexpr(handle, 'eval-fails')) 161 sleep 10m 162 call assert_equal([-4, 'ERROR'], ch_evalexpr(handle, 'eval-result')) 163 164 " Send an eval request that works but can't be encoded. 165 call assert_equal('ok', ch_evalexpr(handle, 'eval-error')) 166 sleep 10m 167 call assert_equal([-5, 'ERROR'], ch_evalexpr(handle, 'eval-result')) 168 169 " Send a bad eval request. There will be no response. 170 call assert_equal('ok', ch_evalexpr(handle, 'eval-bad')) 171 sleep 10m 172 call assert_equal([-5, 'ERROR'], ch_evalexpr(handle, 'eval-result')) 173 174 " Send an expr request 175 call assert_equal('ok', ch_evalexpr(handle, 'an expr')) 176 call WaitFor('"three" == getline("$")') 177 call assert_equal('one', getline(line('$') - 2)) 178 call assert_equal('two', getline(line('$') - 1)) 179 call assert_equal('three', getline('$')) 180 181 " Request a redraw, we don't check for the effect. 182 call assert_equal('ok', ch_evalexpr(handle, 'redraw')) 183 call assert_equal('ok', ch_evalexpr(handle, 'redraw!')) 184 185 call assert_equal('ok', ch_evalexpr(handle, 'empty-request')) 186 187 " Reading while there is nothing available. 188 call assert_equal(v:none, ch_read(handle, {'timeout': 0})) 189 let start = reltime() 190 call assert_equal(v:none, ch_read(handle, {'timeout': 333})) 191 let elapsed = reltime(start) 192 call assert_true(reltimefloat(elapsed) > 0.3) 193 call assert_true(reltimefloat(elapsed) < 0.6) 194 195 " Send without waiting for a response, then wait for a response. 196 call ch_sendexpr(handle, 'wait a bit') 197 let resp = ch_read(handle) 198 call assert_equal(type([]), type(resp)) 199 call assert_equal(type(11), type(resp[0])) 200 call assert_equal('waited', resp[1]) 201 202 " make the server quit, can't check if this works, should not hang. 203 call ch_sendexpr(handle, '!quit!') 204endfunc 205 206func Test_communicate() 207 call ch_log('Test_communicate()') 208 call s:run_server('Ch_communicate') 209endfunc 210 211" Test that we can open two channels. 212func Ch_two_channels(port) 213 let handle = ch_open('localhost:' . a:port, s:chopt) 214 call assert_equal(v:t_channel, type(handle)) 215 if ch_status(handle) == "fail" 216 call assert_report("Can't open channel") 217 return 218 endif 219 220 call assert_equal('got it', ch_evalexpr(handle, 'hello!')) 221 222 let newhandle = ch_open('localhost:' . a:port, s:chopt) 223 if ch_status(newhandle) == "fail" 224 call assert_report("Can't open second channel") 225 return 226 endif 227 call assert_equal('got it', ch_evalexpr(newhandle, 'hello!')) 228 call assert_equal('got it', ch_evalexpr(handle, 'hello!')) 229 230 call ch_close(handle) 231 call assert_equal('got it', ch_evalexpr(newhandle, 'hello!')) 232 233 call ch_close(newhandle) 234endfunc 235 236func Test_two_channels() 237 call ch_log('Test_two_channels()') 238 call s:run_server('Ch_two_channels') 239endfunc 240 241" Test that a server crash is handled gracefully. 242func Ch_server_crash(port) 243 let handle = ch_open('localhost:' . a:port, s:chopt) 244 if ch_status(handle) == "fail" 245 call assert_report("Can't open channel") 246 return 247 endif 248 249 call ch_evalexpr(handle, '!crash!') 250 251 sleep 10m 252endfunc 253 254func Test_server_crash() 255 call ch_log('Test_server_crash()') 256 call s:run_server('Ch_server_crash') 257endfunc 258 259""""""""" 260 261func Ch_handler(chan, msg) 262 call ch_log('Ch_handler()') 263 unlet g:Ch_reply 264 let g:Ch_reply = a:msg 265endfunc 266 267func Ch_channel_handler(port) 268 let handle = ch_open('localhost:' . a:port, s:chopt) 269 if ch_status(handle) == "fail" 270 call assert_report("Can't open channel") 271 return 272 endif 273 274 " Test that it works while waiting on a numbered message. 275 call assert_equal('ok', ch_evalexpr(handle, 'call me')) 276 call WaitFor('"we called you" == g:Ch_reply') 277 278 " Test that it works while not waiting on a numbered message. 279 call ch_sendexpr(handle, 'call me again') 280 call WaitFor('"we did call you" == g:Ch_reply') 281endfunc 282 283func Test_channel_handler() 284 call ch_log('Test_channel_handler()') 285 let g:Ch_reply = "" 286 let s:chopt.callback = 'Ch_handler' 287 call s:run_server('Ch_channel_handler') 288 let g:Ch_reply = "" 289 let s:chopt.callback = function('Ch_handler') 290 call s:run_server('Ch_channel_handler') 291 unlet s:chopt.callback 292endfunc 293 294""""""""" 295 296let g:Ch_reply = '' 297func Ch_zeroHandler(chan, msg) 298 unlet g:Ch_reply 299 let g:Ch_reply = a:msg 300endfunc 301 302let g:Ch_zero_reply = '' 303func Ch_oneHandler(chan, msg) 304 unlet g:Ch_zero_reply 305 let g:Ch_zero_reply = a:msg 306endfunc 307 308func Ch_channel_zero(port) 309 let handle = ch_open('localhost:' . a:port, s:chopt) 310 if ch_status(handle) == "fail" 311 call assert_report("Can't open channel") 312 return 313 endif 314 315 " Check that eval works. 316 call assert_equal('got it', ch_evalexpr(handle, 'hello!')) 317 318 " Check that eval works if a zero id message is sent back. 319 let g:Ch_reply = '' 320 call assert_equal('sent zero', ch_evalexpr(handle, 'send zero')) 321 if s:has_handler 322 call WaitFor('"zero index" == g:Ch_reply') 323 else 324 sleep 20m 325 call assert_equal('', g:Ch_reply) 326 endif 327 328 " Check that handler works if a zero id message is sent back. 329 let g:Ch_reply = '' 330 let g:Ch_zero_reply = '' 331 call ch_sendexpr(handle, 'send zero', {'callback': 'Ch_oneHandler'}) 332 call WaitFor('"sent zero" == g:Ch_zero_reply') 333 if s:has_handler 334 call assert_equal('zero index', g:Ch_reply) 335 else 336 call assert_equal('', g:Ch_reply) 337 endif 338endfunc 339 340func Test_zero_reply() 341 call ch_log('Test_zero_reply()') 342 " Run with channel handler 343 let s:has_handler = 1 344 let s:chopt.callback = 'Ch_zeroHandler' 345 call s:run_server('Ch_channel_zero') 346 unlet s:chopt.callback 347 348 " Run without channel handler 349 let s:has_handler = 0 350 call s:run_server('Ch_channel_zero') 351endfunc 352 353""""""""" 354 355let g:Ch_reply1 = "" 356func Ch_handleRaw1(chan, msg) 357 unlet g:Ch_reply1 358 let g:Ch_reply1 = a:msg 359endfunc 360 361let g:Ch_reply2 = "" 362func Ch_handleRaw2(chan, msg) 363 unlet g:Ch_reply2 364 let g:Ch_reply2 = a:msg 365endfunc 366 367let g:Ch_reply3 = "" 368func Ch_handleRaw3(chan, msg) 369 unlet g:Ch_reply3 370 let g:Ch_reply3 = a:msg 371endfunc 372 373func Ch_raw_one_time_callback(port) 374 let handle = ch_open('localhost:' . a:port, s:chopt) 375 if ch_status(handle) == "fail" 376 call assert_report("Can't open channel") 377 return 378 endif 379 call ch_setoptions(handle, {'mode': 'raw'}) 380 381 " The messages are sent raw, we do our own JSON strings here. 382 call ch_sendraw(handle, "[1, \"hello!\"]\n", {'callback': 'Ch_handleRaw1'}) 383 call WaitFor('g:Ch_reply1 != ""') 384 call assert_equal("[1, \"got it\"]", g:Ch_reply1) 385 call ch_sendraw(handle, "[2, \"echo something\"]\n", {'callback': 'Ch_handleRaw2'}) 386 call ch_sendraw(handle, "[3, \"wait a bit\"]\n", {'callback': 'Ch_handleRaw3'}) 387 call WaitFor('g:Ch_reply2 != ""') 388 call assert_equal("[2, \"something\"]", g:Ch_reply2) 389 " wait for the 200 msec delayed reply 390 call WaitFor('g:Ch_reply3 != ""') 391 call assert_equal("[3, \"waited\"]", g:Ch_reply3) 392endfunc 393 394func Test_raw_one_time_callback() 395 call ch_log('Test_raw_one_time_callback()') 396 call s:run_server('Ch_raw_one_time_callback') 397endfunc 398 399""""""""" 400 401" Test that trying to connect to a non-existing port fails quickly. 402func Test_connect_waittime() 403 call ch_log('Test_connect_waittime()') 404 let start = reltime() 405 let handle = ch_open('localhost:9876', s:chopt) 406 if ch_status(handle) != "fail" 407 " Oops, port does exists. 408 call ch_close(handle) 409 else 410 let elapsed = reltime(start) 411 call assert_true(reltimefloat(elapsed) < 1.0) 412 endif 413 414 " We intend to use a socket that doesn't exist and wait for half a second 415 " before giving up. If the socket does exist it can fail in various ways. 416 " Check for "Connection reset by peer" to avoid flakyness. 417 let start = reltime() 418 try 419 let handle = ch_open('localhost:9867', {'waittime': 500}) 420 if ch_status(handle) != "fail" 421 " Oops, port does exists. 422 call ch_close(handle) 423 else 424 " Failed connection should wait about 500 msec. Can be longer if the 425 " computer is busy with other things. 426 let elapsed = reltime(start) 427 call assert_true(reltimefloat(elapsed) > 0.3) 428 call assert_true(reltimefloat(elapsed) < 1.5) 429 endif 430 catch 431 if v:exception !~ 'Connection reset by peer' 432 call assert_report("Caught exception: " . v:exception) 433 endif 434 endtry 435endfunc 436 437""""""""" 438 439func Test_raw_pipe() 440 if !has('job') 441 return 442 endif 443 call ch_log('Test_raw_pipe()') 444 " Add a dummy close callback to avoid that messages are dropped when calling 445 " ch_canread(). 446 let job = job_start(s:python . " test_channel_pipe.py", 447 \ {'mode': 'raw', 'drop': 'never'}) 448 call assert_equal(v:t_job, type(job)) 449 call assert_equal("run", job_status(job)) 450 451 call assert_equal("open", ch_status(job)) 452 call assert_equal("open", ch_status(job), {"part": "out"}) 453 call assert_equal("open", ch_status(job), {"part": "err"}) 454 call assert_fails('call ch_status(job, {"in_mode": "raw"})', 'E475:') 455 call assert_fails('call ch_status(job, {"part": "in"})', 'E475:') 456 457 let dict = ch_info(job) 458 call assert_true(dict.id != 0) 459 call assert_equal('open', dict.status) 460 call assert_equal('open', dict.out_status) 461 call assert_equal('RAW', dict.out_mode) 462 call assert_equal('pipe', dict.out_io) 463 call assert_equal('open', dict.err_status) 464 call assert_equal('RAW', dict.err_mode) 465 call assert_equal('pipe', dict.err_io) 466 467 try 468 " For a change use the job where a channel is expected. 469 call ch_sendraw(job, "echo something\n") 470 let msg = ch_readraw(job) 471 call assert_equal("something\n", substitute(msg, "\r", "", 'g')) 472 473 call ch_sendraw(job, "double this\n") 474 let g:handle = job_getchannel(job) 475 call WaitFor('ch_canread(g:handle)') 476 unlet g:handle 477 let msg = ch_readraw(job) 478 call assert_equal("this\nAND this\n", substitute(msg, "\r", "", 'g')) 479 480 let g:Ch_reply = "" 481 call ch_sendraw(job, "double this\n", {'callback': 'Ch_handler'}) 482 call WaitFor('"" != g:Ch_reply') 483 call assert_equal("this\nAND this\n", substitute(g:Ch_reply, "\r", "", 'g')) 484 485 let reply = ch_evalraw(job, "quit\n", {'timeout': 100}) 486 call assert_equal("Goodbye!\n", substitute(reply, "\r", "", 'g')) 487 finally 488 call job_stop(job) 489 endtry 490 491 let g:Ch_job = job 492 call WaitFor('"dead" == job_status(g:Ch_job)') 493 let info = job_info(job) 494 call assert_equal("dead", info.status) 495 call assert_equal("term", info.stoponexit) 496endfunc 497 498func Test_nl_pipe() 499 if !has('job') 500 return 501 endif 502 call ch_log('Test_nl_pipe()') 503 let job = job_start([s:python, "test_channel_pipe.py"]) 504 call assert_equal("run", job_status(job)) 505 try 506 let handle = job_getchannel(job) 507 call ch_sendraw(handle, "echo something\n") 508 call assert_equal("something", ch_readraw(handle)) 509 510 call ch_sendraw(handle, "echoerr wrong\n") 511 call assert_equal("wrong", ch_readraw(handle, {'part': 'err'})) 512 513 call ch_sendraw(handle, "double this\n") 514 call assert_equal("this", ch_readraw(handle)) 515 call assert_equal("AND this", ch_readraw(handle)) 516 517 call ch_sendraw(handle, "split this line\n") 518 call assert_equal("this linethis linethis line", ch_readraw(handle)) 519 520 let reply = ch_evalraw(handle, "quit\n") 521 call assert_equal("Goodbye!", reply) 522 finally 523 call job_stop(job) 524 endtry 525endfunc 526 527func Test_nl_err_to_out_pipe() 528 if !has('job') 529 return 530 endif 531 call ch_logfile('Xlog') 532 call ch_log('Test_nl_err_to_out_pipe()') 533 let job = job_start(s:python . " test_channel_pipe.py", {'err_io': 'out'}) 534 call assert_equal("run", job_status(job)) 535 try 536 let handle = job_getchannel(job) 537 call ch_sendraw(handle, "echo something\n") 538 call assert_equal("something", ch_readraw(handle)) 539 540 call ch_sendraw(handle, "echoerr wrong\n") 541 call assert_equal("wrong", ch_readraw(handle)) 542 finally 543 call job_stop(job) 544 call ch_logfile('') 545 let loglines = readfile('Xlog') 546 call assert_true(len(loglines) > 10) 547 let found_test = 0 548 let found_send = 0 549 let found_recv = 0 550 let found_stop = 0 551 for l in loglines 552 if l =~ 'Test_nl_err_to_out_pipe' 553 let found_test = 1 554 endif 555 if l =~ 'SEND on.*echo something' 556 let found_send = 1 557 endif 558 if l =~ 'RECV on.*something' 559 let found_recv = 1 560 endif 561 if l =~ 'Stopping job with' 562 let found_stop = 1 563 endif 564 endfor 565 call assert_equal(1, found_test) 566 call assert_equal(1, found_send) 567 call assert_equal(1, found_recv) 568 call assert_equal(1, found_stop) 569 " On MS-Windows need to sleep for a moment to be able to delete the file. 570 sleep 10m 571 call delete('Xlog') 572 endtry 573endfunc 574 575func Stop_g_job() 576 call job_stop(g:job) 577 if has('win32') 578 " On MS-Windows the server must close the file handle before we are able 579 " to delete the file. 580 call WaitFor('job_status(g:job) == "dead"') 581 sleep 10m 582 endif 583endfunc 584 585func Test_nl_read_file() 586 if !has('job') 587 return 588 endif 589 call ch_log('Test_nl_read_file()') 590 call writefile(['echo something', 'echoerr wrong', 'double this'], 'Xinput') 591 let g:job = job_start(s:python . " test_channel_pipe.py", 592 \ {'in_io': 'file', 'in_name': 'Xinput'}) 593 call assert_equal("run", job_status(g:job)) 594 try 595 let handle = job_getchannel(g:job) 596 call assert_equal("something", ch_readraw(handle)) 597 call assert_equal("wrong", ch_readraw(handle, {'part': 'err'})) 598 call assert_equal("this", ch_readraw(handle)) 599 call assert_equal("AND this", ch_readraw(handle)) 600 finally 601 call Stop_g_job() 602 call delete('Xinput') 603 endtry 604endfunc 605 606func Test_nl_write_out_file() 607 if !has('job') 608 return 609 endif 610 call ch_log('Test_nl_write_out_file()') 611 let g:job = job_start(s:python . " test_channel_pipe.py", 612 \ {'out_io': 'file', 'out_name': 'Xoutput'}) 613 call assert_equal("run", job_status(g:job)) 614 try 615 let handle = job_getchannel(g:job) 616 call ch_sendraw(handle, "echo line one\n") 617 call ch_sendraw(handle, "echo line two\n") 618 call ch_sendraw(handle, "double this\n") 619 call WaitFor('len(readfile("Xoutput")) > 2') 620 call assert_equal(['line one', 'line two', 'this', 'AND this'], readfile('Xoutput')) 621 finally 622 call Stop_g_job() 623 call delete('Xoutput') 624 endtry 625endfunc 626 627func Test_nl_write_err_file() 628 if !has('job') 629 return 630 endif 631 call ch_log('Test_nl_write_err_file()') 632 let g:job = job_start(s:python . " test_channel_pipe.py", 633 \ {'err_io': 'file', 'err_name': 'Xoutput'}) 634 call assert_equal("run", job_status(g:job)) 635 try 636 let handle = job_getchannel(g:job) 637 call ch_sendraw(handle, "echoerr line one\n") 638 call ch_sendraw(handle, "echoerr line two\n") 639 call ch_sendraw(handle, "doubleerr this\n") 640 call WaitFor('len(readfile("Xoutput")) > 2') 641 call assert_equal(['line one', 'line two', 'this', 'AND this'], readfile('Xoutput')) 642 finally 643 call Stop_g_job() 644 call delete('Xoutput') 645 endtry 646endfunc 647 648func Test_nl_write_both_file() 649 if !has('job') 650 return 651 endif 652 call ch_log('Test_nl_write_both_file()') 653 let g:job = job_start(s:python . " test_channel_pipe.py", 654 \ {'out_io': 'file', 'out_name': 'Xoutput', 'err_io': 'out'}) 655 call assert_equal("run", job_status(g:job)) 656 try 657 let handle = job_getchannel(g:job) 658 call ch_sendraw(handle, "echoerr line one\n") 659 call ch_sendraw(handle, "echo line two\n") 660 call ch_sendraw(handle, "double this\n") 661 call ch_sendraw(handle, "doubleerr that\n") 662 call WaitFor('len(readfile("Xoutput")) > 5') 663 call assert_equal(['line one', 'line two', 'this', 'AND this', 'that', 'AND that'], readfile('Xoutput')) 664 finally 665 call Stop_g_job() 666 call delete('Xoutput') 667 endtry 668endfunc 669 670func BufCloseCb(ch) 671 let g:Ch_bufClosed = 'yes' 672endfunc 673 674func Run_test_pipe_to_buffer(use_name, nomod, do_msg) 675 if !has('job') 676 return 677 endif 678 call ch_log('Test_pipe_to_buffer()') 679 let g:Ch_bufClosed = 'no' 680 let options = {'out_io': 'buffer', 'close_cb': 'BufCloseCb'} 681 let expected = ['', 'line one', 'line two', 'this', 'AND this', 'Goodbye!'] 682 if a:use_name 683 let options['out_name'] = 'pipe-output' 684 if a:do_msg 685 let expected[0] = 'Reading from channel output...' 686 else 687 let options['out_msg'] = 0 688 call remove(expected, 0) 689 endif 690 else 691 sp pipe-output 692 let options['out_buf'] = bufnr('%') 693 quit 694 call remove(expected, 0) 695 endif 696 if a:nomod 697 let options['out_modifiable'] = 0 698 endif 699 let job = job_start(s:python . " test_channel_pipe.py", options) 700 call assert_equal("run", job_status(job)) 701 try 702 let handle = job_getchannel(job) 703 call ch_sendraw(handle, "echo line one\n") 704 call ch_sendraw(handle, "echo line two\n") 705 call ch_sendraw(handle, "double this\n") 706 call ch_sendraw(handle, "quit\n") 707 sp pipe-output 708 call WaitFor('line("$") == ' . len(expected) . ' && g:Ch_bufClosed == "yes"') 709 call assert_equal(expected, getline(1, '$')) 710 if a:nomod 711 call assert_equal(0, &modifiable) 712 else 713 call assert_equal(1, &modifiable) 714 endif 715 call assert_equal('yes', g:Ch_bufClosed) 716 bwipe! 717 finally 718 call job_stop(job) 719 endtry 720endfunc 721 722func Test_pipe_to_buffer_name() 723 call Run_test_pipe_to_buffer(1, 0, 1) 724endfunc 725 726func Test_pipe_to_buffer_nr() 727 call Run_test_pipe_to_buffer(0, 0, 1) 728endfunc 729 730func Test_pipe_to_buffer_name_nomod() 731 call Run_test_pipe_to_buffer(1, 1, 1) 732endfunc 733 734func Test_pipe_to_buffer_name_nomsg() 735 call Run_test_pipe_to_buffer(1, 0, 1) 736endfunc 737 738func Test_close_output_buffer() 739 if !has('job') 740 return 741 endif 742 enew! 743 let test_lines = ['one', 'two'] 744 call setline(1, test_lines) 745 call ch_log('Test_close_output_buffer()') 746 let options = {'out_io': 'buffer'} 747 let options['out_name'] = 'buffer-output' 748 let options['out_msg'] = 0 749 split buffer-output 750 let job = job_start(s:python . " test_channel_write.py", options) 751 call assert_equal("run", job_status(job)) 752 try 753 call WaitFor('line("$") == 3') 754 call assert_equal(3, line('$')) 755 quit! 756 sleep 100m 757 " Make sure the write didn't happen to the wrong buffer. 758 call assert_equal(test_lines, getline(1, line('$'))) 759 call assert_equal(-1, bufwinnr('buffer-output')) 760 sbuf buffer-output 761 call assert_notequal(-1, bufwinnr('buffer-output')) 762 sleep 100m 763 close " no more writes 764 bwipe! 765 finally 766 call job_stop(job) 767 endtry 768endfunc 769 770func Run_test_pipe_err_to_buffer(use_name, nomod, do_msg) 771 if !has('job') 772 return 773 endif 774 call ch_log('Test_pipe_err_to_buffer()') 775 let options = {'err_io': 'buffer'} 776 let expected = ['', 'line one', 'line two', 'this', 'AND this'] 777 if a:use_name 778 let options['err_name'] = 'pipe-err' 779 if a:do_msg 780 let expected[0] = 'Reading from channel error...' 781 else 782 let options['err_msg'] = 0 783 call remove(expected, 0) 784 endif 785 else 786 sp pipe-err 787 let options['err_buf'] = bufnr('%') 788 quit 789 call remove(expected, 0) 790 endif 791 if a:nomod 792 let options['err_modifiable'] = 0 793 endif 794 let job = job_start(s:python . " test_channel_pipe.py", options) 795 call assert_equal("run", job_status(job)) 796 try 797 let handle = job_getchannel(job) 798 call ch_sendraw(handle, "echoerr line one\n") 799 call ch_sendraw(handle, "echoerr line two\n") 800 call ch_sendraw(handle, "doubleerr this\n") 801 call ch_sendraw(handle, "quit\n") 802 sp pipe-err 803 call WaitFor('line("$") == ' . len(expected)) 804 call assert_equal(expected, getline(1, '$')) 805 if a:nomod 806 call assert_equal(0, &modifiable) 807 else 808 call assert_equal(1, &modifiable) 809 endif 810 bwipe! 811 finally 812 call job_stop(job) 813 endtry 814endfunc 815 816func Test_pipe_err_to_buffer_name() 817 call Run_test_pipe_err_to_buffer(1, 0, 1) 818endfunc 819 820func Test_pipe_err_to_buffer_nr() 821 call Run_test_pipe_err_to_buffer(0, 0, 1) 822endfunc 823 824func Test_pipe_err_to_buffer_name_nomod() 825 call Run_test_pipe_err_to_buffer(1, 1, 1) 826endfunc 827 828func Test_pipe_err_to_buffer_name_nomsg() 829 call Run_test_pipe_err_to_buffer(1, 0, 0) 830endfunc 831 832func Test_pipe_both_to_buffer() 833 if !has('job') 834 return 835 endif 836 call ch_log('Test_pipe_both_to_buffer()') 837 let job = job_start(s:python . " test_channel_pipe.py", 838 \ {'out_io': 'buffer', 'out_name': 'pipe-err', 'err_io': 'out'}) 839 call assert_equal("run", job_status(job)) 840 try 841 let handle = job_getchannel(job) 842 call ch_sendraw(handle, "echo line one\n") 843 call ch_sendraw(handle, "echoerr line two\n") 844 call ch_sendraw(handle, "double this\n") 845 call ch_sendraw(handle, "doubleerr that\n") 846 call ch_sendraw(handle, "quit\n") 847 sp pipe-err 848 call WaitFor('line("$") >= 7') 849 call assert_equal(['Reading from channel output...', 'line one', 'line two', 'this', 'AND this', 'that', 'AND that', 'Goodbye!'], getline(1, '$')) 850 bwipe! 851 finally 852 call job_stop(job) 853 endtry 854endfunc 855 856func Run_test_pipe_from_buffer(use_name) 857 if !has('job') 858 return 859 endif 860 call ch_log('Test_pipe_from_buffer()') 861 862 sp pipe-input 863 call setline(1, ['echo one', 'echo two', 'echo three']) 864 let options = {'in_io': 'buffer', 'block_write': 1} 865 if a:use_name 866 let options['in_name'] = 'pipe-input' 867 else 868 let options['in_buf'] = bufnr('%') 869 endif 870 871 let job = job_start(s:python . " test_channel_pipe.py", options) 872 call assert_equal("run", job_status(job)) 873 try 874 let handle = job_getchannel(job) 875 call assert_equal('one', ch_read(handle)) 876 call assert_equal('two', ch_read(handle)) 877 call assert_equal('three', ch_read(handle)) 878 bwipe! 879 finally 880 call job_stop(job) 881 endtry 882endfunc 883 884func Test_pipe_from_buffer_name() 885 call Run_test_pipe_from_buffer(1) 886endfunc 887 888func Test_pipe_from_buffer_nr() 889 call Run_test_pipe_from_buffer(0) 890endfunc 891 892func Run_pipe_through_sort(all, use_buffer) 893 if !executable('sort') || !has('job') 894 return 895 endif 896 let options = {'out_io': 'buffer', 'out_name': 'sortout'} 897 if a:use_buffer 898 split sortin 899 call setline(1, ['ccc', 'aaa', 'ddd', 'bbb', 'eee']) 900 let options.in_io = 'buffer' 901 let options.in_name = 'sortin' 902 endif 903 if !a:all 904 let options.in_top = 2 905 let options.in_bot = 4 906 endif 907 let g:job = job_start('sort', options) 908 call assert_equal("run", job_status(g:job)) 909 910 if !a:use_buffer 911 call ch_sendraw(g:job, "ccc\naaa\nddd\nbbb\neee\n") 912 call ch_close_in(g:job) 913 endif 914 915 call WaitFor('job_status(g:job) == "dead"') 916 call assert_equal("dead", job_status(g:job)) 917 918 sp sortout 919 call WaitFor('line("$") > 3') 920 call assert_equal('Reading from channel output...', getline(1)) 921 if a:all 922 call assert_equal(['aaa', 'bbb', 'ccc', 'ddd', 'eee'], getline(2, 6)) 923 else 924 call assert_equal(['aaa', 'bbb', 'ddd'], getline(2, 4)) 925 endif 926 927 call job_stop(g:job) 928 unlet g:job 929 if a:use_buffer 930 bwipe! sortin 931 endif 932 bwipe! sortout 933endfunc 934 935func Test_pipe_through_sort_all() 936 call ch_log('Test_pipe_through_sort_all()') 937 call Run_pipe_through_sort(1, 1) 938endfunc 939 940func Test_pipe_through_sort_some() 941 call ch_log('Test_pipe_through_sort_some()') 942 call Run_pipe_through_sort(0, 1) 943endfunc 944 945func Test_pipe_through_sort_feed() 946 call ch_log('Test_pipe_through_sort_feed()') 947 call Run_pipe_through_sort(1, 0) 948endfunc 949 950func Test_pipe_to_nameless_buffer() 951 if !has('job') 952 return 953 endif 954 call ch_log('Test_pipe_to_nameless_buffer()') 955 let job = job_start(s:python . " test_channel_pipe.py", 956 \ {'out_io': 'buffer'}) 957 call assert_equal("run", job_status(job)) 958 try 959 let handle = job_getchannel(job) 960 call ch_sendraw(handle, "echo line one\n") 961 call ch_sendraw(handle, "echo line two\n") 962 exe ch_getbufnr(handle, "out") . 'sbuf' 963 call WaitFor('line("$") >= 3') 964 call assert_equal(['Reading from channel output...', 'line one', 'line two'], getline(1, '$')) 965 bwipe! 966 finally 967 call job_stop(job) 968 endtry 969endfunc 970 971func Test_pipe_to_buffer_json() 972 if !has('job') 973 return 974 endif 975 call ch_log('Test_pipe_to_buffer_json()') 976 let job = job_start(s:python . " test_channel_pipe.py", 977 \ {'out_io': 'buffer', 'out_mode': 'json'}) 978 call assert_equal("run", job_status(job)) 979 try 980 let handle = job_getchannel(job) 981 call ch_sendraw(handle, "echo [0, \"hello\"]\n") 982 call ch_sendraw(handle, "echo [-2, 12.34]\n") 983 exe ch_getbufnr(handle, "out") . 'sbuf' 984 call WaitFor('line("$") >= 3') 985 call assert_equal(['Reading from channel output...', '[0,"hello"]', '[-2,12.34]'], getline(1, '$')) 986 bwipe! 987 finally 988 call job_stop(job) 989 endtry 990endfunc 991 992" Wait a little while for the last line, minus "offset", to equal "line". 993func s:wait_for_last_line(line, offset) 994 for i in range(100) 995 if getline(line('$') - a:offset) == a:line 996 break 997 endif 998 sleep 10m 999 endfor 1000endfunc 1001 1002func Test_pipe_io_two_buffers() 1003 if !has('job') 1004 return 1005 endif 1006 call ch_log('Test_pipe_io_two_buffers()') 1007 1008 " Create two buffers, one to read from and one to write to. 1009 split pipe-output 1010 set buftype=nofile 1011 split pipe-input 1012 set buftype=nofile 1013 1014 let job = job_start(s:python . " test_channel_pipe.py", 1015 \ {'in_io': 'buffer', 'in_name': 'pipe-input', 'in_top': 0, 1016 \ 'out_io': 'buffer', 'out_name': 'pipe-output', 1017 \ 'block_write': 1}) 1018 call assert_equal("run", job_status(job)) 1019 try 1020 exe "normal Gaecho hello\<CR>" 1021 exe bufwinnr('pipe-output') . "wincmd w" 1022 call s:wait_for_last_line('hello', 0) 1023 call assert_equal('hello', getline('$')) 1024 1025 exe bufwinnr('pipe-input') . "wincmd w" 1026 exe "normal Gadouble this\<CR>" 1027 exe bufwinnr('pipe-output') . "wincmd w" 1028 call s:wait_for_last_line('AND this', 0) 1029 call assert_equal('this', getline(line('$') - 1)) 1030 call assert_equal('AND this', getline('$')) 1031 1032 bwipe! 1033 exe bufwinnr('pipe-input') . "wincmd w" 1034 bwipe! 1035 finally 1036 call job_stop(job) 1037 endtry 1038endfunc 1039 1040func Test_pipe_io_one_buffer() 1041 if !has('job') 1042 return 1043 endif 1044 call ch_log('Test_pipe_io_one_buffer()') 1045 1046 " Create one buffer to read from and to write to. 1047 split pipe-io 1048 set buftype=nofile 1049 1050 let job = job_start(s:python . " test_channel_pipe.py", 1051 \ {'in_io': 'buffer', 'in_name': 'pipe-io', 'in_top': 0, 1052 \ 'out_io': 'buffer', 'out_name': 'pipe-io', 1053 \ 'block_write': 1}) 1054 call assert_equal("run", job_status(job)) 1055 try 1056 exe "normal Goecho hello\<CR>" 1057 call s:wait_for_last_line('hello', 1) 1058 call assert_equal('hello', getline(line('$') - 1)) 1059 1060 exe "normal Gadouble this\<CR>" 1061 call s:wait_for_last_line('AND this', 1) 1062 call assert_equal('this', getline(line('$') - 2)) 1063 call assert_equal('AND this', getline(line('$') - 1)) 1064 1065 bwipe! 1066 finally 1067 call job_stop(job) 1068 endtry 1069endfunc 1070 1071func Test_pipe_null() 1072 if !has('job') 1073 return 1074 endif 1075 call ch_log('Test_pipe_null()') 1076 1077 " We cannot check that no I/O works, we only check that the job starts 1078 " properly. 1079 let job = job_start(s:python . " test_channel_pipe.py something", 1080 \ {'in_io': 'null'}) 1081 call assert_equal("run", job_status(job)) 1082 try 1083 call assert_equal('something', ch_read(job)) 1084 finally 1085 call job_stop(job) 1086 endtry 1087 1088 let job = job_start(s:python . " test_channel_pipe.py err-out", 1089 \ {'out_io': 'null'}) 1090 call assert_equal("run", job_status(job)) 1091 try 1092 call assert_equal('err-out', ch_read(job, {"part": "err"})) 1093 finally 1094 call job_stop(job) 1095 endtry 1096 1097 let job = job_start(s:python . " test_channel_pipe.py something", 1098 \ {'err_io': 'null'}) 1099 call assert_equal("run", job_status(job)) 1100 try 1101 call assert_equal('something', ch_read(job)) 1102 finally 1103 call job_stop(job) 1104 endtry 1105 1106 let job = job_start(s:python . " test_channel_pipe.py something", 1107 \ {'out_io': 'null', 'err_io': 'out'}) 1108 call assert_equal("run", job_status(job)) 1109 call job_stop(job) 1110 1111 let job = job_start(s:python . " test_channel_pipe.py something", 1112 \ {'in_io': 'null', 'out_io': 'null', 'err_io': 'null'}) 1113 call assert_equal("run", job_status(job)) 1114 call assert_equal('channel fail', string(job_getchannel(job))) 1115 call assert_equal('fail', ch_status(job)) 1116 call job_stop(job) 1117endfunc 1118 1119func Test_pipe_to_buffer_raw() 1120 if !has('job') 1121 return 1122 endif 1123 call ch_log('Test_raw_pipe_to_buffer()') 1124 let options = {'out_mode': 'raw', 'out_io': 'buffer', 'out_name': 'testout'} 1125 split testout 1126 let job = job_start([s:python, '-c', 1127 \ 'import sys; [sys.stdout.write(".") and sys.stdout.flush() for _ in range(10000)]'], options) 1128 call assert_equal("run", job_status(job)) 1129 call WaitFor('len(join(getline(1, "$"), "")) >= 10000', 3000) 1130 try 1131 let totlen = 0 1132 for line in getline(1, '$') 1133 call assert_equal('', substitute(line, '^\.*', '', '')) 1134 let totlen += len(line) 1135 endfor 1136 call assert_equal(10000, totlen) 1137 finally 1138 call job_stop(job) 1139 bwipe! 1140 endtry 1141endfunc 1142 1143func Test_reuse_channel() 1144 if !has('job') 1145 return 1146 endif 1147 call ch_log('Test_reuse_channel()') 1148 1149 let job = job_start(s:python . " test_channel_pipe.py") 1150 call assert_equal("run", job_status(job)) 1151 let handle = job_getchannel(job) 1152 try 1153 call ch_sendraw(handle, "echo something\n") 1154 call assert_equal("something", ch_readraw(handle)) 1155 finally 1156 call job_stop(job) 1157 endtry 1158 1159 let job = job_start(s:python . " test_channel_pipe.py", {'channel': handle}) 1160 call assert_equal("run", job_status(job)) 1161 let handle = job_getchannel(job) 1162 try 1163 call ch_sendraw(handle, "echo again\n") 1164 call assert_equal("again", ch_readraw(handle)) 1165 finally 1166 call job_stop(job) 1167 endtry 1168endfunc 1169 1170func Test_out_cb() 1171 if !has('job') 1172 return 1173 endif 1174 call ch_log('Test_out_cb()') 1175 1176 let dict = {'thisis': 'dict: '} 1177 func dict.outHandler(chan, msg) dict 1178 if type(a:msg) == v:t_string 1179 let g:Ch_outmsg = self.thisis . a:msg 1180 else 1181 let g:Ch_outobj = a:msg 1182 endif 1183 endfunc 1184 func dict.errHandler(chan, msg) dict 1185 let g:Ch_errmsg = self.thisis . a:msg 1186 endfunc 1187 let job = job_start(s:python . " test_channel_pipe.py", 1188 \ {'out_cb': dict.outHandler, 1189 \ 'out_mode': 'json', 1190 \ 'err_cb': dict.errHandler, 1191 \ 'err_mode': 'json'}) 1192 call assert_equal("run", job_status(job)) 1193 try 1194 let g:Ch_outmsg = '' 1195 let g:Ch_errmsg = '' 1196 call ch_sendraw(job, "echo [0, \"hello\"]\n") 1197 call ch_sendraw(job, "echoerr [0, \"there\"]\n") 1198 call WaitFor('g:Ch_outmsg != ""') 1199 call assert_equal("dict: hello", g:Ch_outmsg) 1200 call WaitFor('g:Ch_errmsg != ""') 1201 call assert_equal("dict: there", g:Ch_errmsg) 1202 1203 " Receive a json object split in pieces 1204 unlet! g:Ch_outobj 1205 call ch_sendraw(job, "echosplit [0, {\"one\": 1,| \"tw|o\": 2, \"three\": 3|}]\n") 1206 call WaitFor('exists("g:Ch_outobj")') 1207 call assert_equal({'one': 1, 'two': 2, 'three': 3}, g:Ch_outobj) 1208 finally 1209 call job_stop(job) 1210 endtry 1211endfunc 1212 1213func Test_out_close_cb() 1214 if !has('job') 1215 return 1216 endif 1217 call ch_log('Test_out_close_cb()') 1218 1219 let s:counter = 1 1220 let g:Ch_msg1 = '' 1221 let g:Ch_closemsg = 0 1222 func! OutHandler(chan, msg) 1223 if s:counter == 1 1224 let g:Ch_msg1 = a:msg 1225 endif 1226 let s:counter += 1 1227 endfunc 1228 func! CloseHandler(chan) 1229 let g:Ch_closemsg = s:counter 1230 let s:counter += 1 1231 endfunc 1232 let job = job_start(s:python . " test_channel_pipe.py quit now", 1233 \ {'out_cb': 'OutHandler', 1234 \ 'close_cb': 'CloseHandler'}) 1235 call assert_equal("run", job_status(job)) 1236 try 1237 call WaitFor('g:Ch_closemsg != 0 && g:Ch_msg1 != ""') 1238 call assert_equal('quit', g:Ch_msg1) 1239 call assert_equal(2, g:Ch_closemsg) 1240 finally 1241 call job_stop(job) 1242 delfunc OutHandler 1243 delfunc CloseHandler 1244 endtry 1245endfunc 1246 1247func Test_read_in_close_cb() 1248 if !has('job') 1249 return 1250 endif 1251 call ch_log('Test_read_in_close_cb()') 1252 1253 let g:Ch_received = '' 1254 func! CloseHandler(chan) 1255 let g:Ch_received = ch_read(a:chan) 1256 endfunc 1257 let job = job_start(s:python . " test_channel_pipe.py quit now", 1258 \ {'close_cb': 'CloseHandler'}) 1259 call assert_equal("run", job_status(job)) 1260 try 1261 call WaitFor('g:Ch_received != ""') 1262 call assert_equal('quit', g:Ch_received) 1263 finally 1264 call job_stop(job) 1265 delfunc CloseHandler 1266 endtry 1267endfunc 1268 1269func Test_out_cb_lambda() 1270 if !has('job') 1271 return 1272 endif 1273 call ch_log('Test_out_cb_lambda()') 1274 1275 let job = job_start(s:python . " test_channel_pipe.py", 1276 \ {'out_cb': {ch, msg -> execute("let g:Ch_outmsg = 'lambda: ' . msg")}, 1277 \ 'out_mode': 'json', 1278 \ 'err_cb': {ch, msg -> execute(":let g:Ch_errmsg = 'lambda: ' . msg")}, 1279 \ 'err_mode': 'json'}) 1280 call assert_equal("run", job_status(job)) 1281 try 1282 let g:Ch_outmsg = '' 1283 let g:Ch_errmsg = '' 1284 call ch_sendraw(job, "echo [0, \"hello\"]\n") 1285 call ch_sendraw(job, "echoerr [0, \"there\"]\n") 1286 call WaitFor('g:Ch_outmsg != ""') 1287 call assert_equal("lambda: hello", g:Ch_outmsg) 1288 call WaitFor('g:Ch_errmsg != ""') 1289 call assert_equal("lambda: there", g:Ch_errmsg) 1290 finally 1291 call job_stop(job) 1292 endtry 1293endfunc 1294 1295func Test_close_and_exit_cb() 1296 if !has('job') 1297 return 1298 endif 1299 call ch_log('Test_close_and_exit_cb') 1300 1301 let g:retdict = {'ret': {}} 1302 func g:retdict.close_cb(ch) dict 1303 let self.ret['close_cb'] = job_status(ch_getjob(a:ch)) 1304 endfunc 1305 func g:retdict.exit_cb(job, status) dict 1306 let self.ret['exit_cb'] = job_status(a:job) 1307 endfunc 1308 1309 let g:job = job_start('echo', { 1310 \ 'close_cb': g:retdict.close_cb, 1311 \ 'exit_cb': g:retdict.exit_cb, 1312 \ }) 1313 call assert_equal('run', job_status(g:job)) 1314 unlet g:job 1315 call WaitFor('len(g:retdict.ret) >= 2') 1316 call assert_equal(2, len(g:retdict.ret)) 1317 call assert_match('^\%(dead\|run\)', g:retdict.ret['close_cb']) 1318 call assert_equal('dead', g:retdict.ret['exit_cb']) 1319 unlet g:retdict 1320endfunc 1321 1322"""""""""" 1323 1324let g:Ch_unletResponse = '' 1325func s:UnletHandler(handle, msg) 1326 let g:Ch_unletResponse = a:msg 1327 unlet s:channelfd 1328endfunc 1329 1330" Test that "unlet handle" in a handler doesn't crash Vim. 1331func Ch_unlet_handle(port) 1332 let s:channelfd = ch_open('localhost:' . a:port, s:chopt) 1333 call ch_sendexpr(s:channelfd, "test", {'callback': function('s:UnletHandler')}) 1334 call WaitFor('"what?" == g:Ch_unletResponse') 1335 call assert_equal('what?', g:Ch_unletResponse) 1336endfunc 1337 1338func Test_unlet_handle() 1339 call ch_log('Test_unlet_handle()') 1340 call s:run_server('Ch_unlet_handle') 1341endfunc 1342 1343"""""""""" 1344 1345let g:Ch_unletResponse = '' 1346func Ch_CloseHandler(handle, msg) 1347 let g:Ch_unletResponse = a:msg 1348 call ch_close(s:channelfd) 1349endfunc 1350 1351" Test that "unlet handle" in a handler doesn't crash Vim. 1352func Ch_close_handle(port) 1353 let s:channelfd = ch_open('localhost:' . a:port, s:chopt) 1354 call ch_sendexpr(s:channelfd, "test", {'callback': function('Ch_CloseHandler')}) 1355 call WaitFor('"what?" == g:Ch_unletResponse') 1356 call assert_equal('what?', g:Ch_unletResponse) 1357endfunc 1358 1359func Test_close_handle() 1360 call ch_log('Test_close_handle()') 1361 call s:run_server('Ch_close_handle') 1362endfunc 1363 1364"""""""""" 1365 1366func Test_open_fail() 1367 call ch_log('Test_open_fail()') 1368 silent! let ch = ch_open("noserver") 1369 echo ch 1370 let d = ch 1371endfunc 1372 1373"""""""""" 1374 1375func Ch_open_delay(port) 1376 " Wait up to a second for the port to open. 1377 let s:chopt.waittime = 1000 1378 let channel = ch_open('localhost:' . a:port, s:chopt) 1379 unlet s:chopt.waittime 1380 if ch_status(channel) == "fail" 1381 call assert_report("Can't open channel") 1382 return 1383 endif 1384 call assert_equal('got it', ch_evalexpr(channel, 'hello!')) 1385 call ch_close(channel) 1386endfunc 1387 1388func Test_open_delay() 1389 call ch_log('Test_open_delay()') 1390 " The server will wait half a second before creating the port. 1391 call s:run_server('Ch_open_delay', 'delay') 1392endfunc 1393 1394""""""""" 1395 1396function MyFunction(a,b,c) 1397 let g:Ch_call_ret = [a:a, a:b, a:c] 1398endfunc 1399 1400function Ch_test_call(port) 1401 let handle = ch_open('localhost:' . a:port, s:chopt) 1402 if ch_status(handle) == "fail" 1403 call assert_report("Can't open channel") 1404 return 1405 endif 1406 1407 let g:Ch_call_ret = [] 1408 call assert_equal('ok', ch_evalexpr(handle, 'call-func')) 1409 call WaitFor('len(g:Ch_call_ret) > 0') 1410 call assert_equal([1, 2, 3], g:Ch_call_ret) 1411endfunc 1412 1413func Test_call() 1414 call ch_log('Test_call()') 1415 call s:run_server('Ch_test_call') 1416endfunc 1417 1418""""""""" 1419 1420let g:Ch_job_exit_ret = 'not yet' 1421function MyExitCb(job, status) 1422 let g:Ch_job_exit_ret = 'done' 1423endfunc 1424 1425function Ch_test_exit_callback(port) 1426 call job_setoptions(g:currentJob, {'exit_cb': 'MyExitCb'}) 1427 let g:Ch_exit_job = g:currentJob 1428 call assert_equal('MyExitCb', job_info(g:currentJob)['exit_cb']) 1429endfunc 1430 1431func Test_exit_callback() 1432 if has('job') 1433 call ch_log('Test_exit_callback()') 1434 call s:run_server('Ch_test_exit_callback') 1435 1436 " wait up to a second for the job to exit 1437 for i in range(100) 1438 if g:Ch_job_exit_ret == 'done' 1439 break 1440 endif 1441 sleep 10m 1442 " calling job_status() triggers the callback 1443 call job_status(g:Ch_exit_job) 1444 endfor 1445 1446 call assert_equal('done', g:Ch_job_exit_ret) 1447 call assert_equal('dead', job_info(g:Ch_exit_job).status) 1448 unlet g:Ch_exit_job 1449 endif 1450endfunc 1451 1452function MyExitTimeCb(job, status) 1453 if job_info(a:job).process == g:exit_cb_val.process 1454 let g:exit_cb_val.end = reltime(g:exit_cb_val.start) 1455 endif 1456 call Resume() 1457endfunction 1458 1459func Test_exit_callback_interval() 1460 if !has('job') 1461 return 1462 endif 1463 1464 let g:exit_cb_val = {'start': reltime(), 'end': 0, 'process': 0} 1465 let job = job_start([s:python, '-c', 'import time;time.sleep(0.5)'], {'exit_cb': 'MyExitTimeCb'}) 1466 let g:exit_cb_val.process = job_info(job).process 1467 call WaitFor('type(g:exit_cb_val.end) != v:t_number || g:exit_cb_val.end != 0', 2000) 1468 let elapsed = reltimefloat(g:exit_cb_val.end) 1469 call assert_true(elapsed > 0.5) 1470 call assert_true(elapsed < 1.0) 1471 1472 " case: unreferenced job, using timer 1473 if !has('timers') 1474 return 1475 endif 1476 1477 let g:exit_cb_val = {'start': reltime(), 'end': 0, 'process': 0} 1478 let g:job = job_start([s:python, '-c', 'import time;time.sleep(0.5)'], {'exit_cb': 'MyExitTimeCb'}) 1479 let g:exit_cb_val.process = job_info(g:job).process 1480 unlet g:job 1481 call Standby(1000) 1482 if type(g:exit_cb_val.end) != v:t_number || g:exit_cb_val.end != 0 1483 let elapsed = reltimefloat(g:exit_cb_val.end) 1484 else 1485 let elapsed = 1.0 1486 endif 1487 call assert_true(elapsed > 0.5) 1488 call assert_true(elapsed < 1.0) 1489endfunc 1490 1491""""""""" 1492 1493let g:Ch_close_ret = 'alive' 1494function MyCloseCb(ch) 1495 let g:Ch_close_ret = 'closed' 1496endfunc 1497 1498function Ch_test_close_callback(port) 1499 let handle = ch_open('localhost:' . a:port, s:chopt) 1500 if ch_status(handle) == "fail" 1501 call assert_report("Can't open channel") 1502 return 1503 endif 1504 call ch_setoptions(handle, {'close_cb': 'MyCloseCb'}) 1505 1506 call assert_equal('', ch_evalexpr(handle, 'close me')) 1507 call WaitFor('"closed" == g:Ch_close_ret') 1508 call assert_equal('closed', g:Ch_close_ret) 1509endfunc 1510 1511func Test_close_callback() 1512 call ch_log('Test_close_callback()') 1513 call s:run_server('Ch_test_close_callback') 1514endfunc 1515 1516function Ch_test_close_partial(port) 1517 let handle = ch_open('localhost:' . a:port, s:chopt) 1518 if ch_status(handle) == "fail" 1519 call assert_report("Can't open channel") 1520 return 1521 endif 1522 let g:Ch_d = {} 1523 func g:Ch_d.closeCb(ch) dict 1524 let self.close_ret = 'closed' 1525 endfunc 1526 call ch_setoptions(handle, {'close_cb': g:Ch_d.closeCb}) 1527 1528 call assert_equal('', ch_evalexpr(handle, 'close me')) 1529 call WaitFor('"closed" == g:Ch_d.close_ret') 1530 call assert_equal('closed', g:Ch_d.close_ret) 1531 unlet g:Ch_d 1532endfunc 1533 1534func Test_close_partial() 1535 call ch_log('Test_close_partial()') 1536 call s:run_server('Ch_test_close_partial') 1537endfunc 1538 1539func Test_job_start_invalid() 1540 call assert_fails('call job_start($x)', 'E474:') 1541 call assert_fails('call job_start("")', 'E474:') 1542endfunc 1543 1544func Test_job_stop_immediately() 1545 if !has('job') 1546 return 1547 endif 1548 1549 let g:job = job_start([s:python, '-c', 'import time;time.sleep(10)']) 1550 try 1551 call job_stop(g:job) 1552 call WaitFor('"dead" == job_status(g:job)') 1553 call assert_equal('dead', job_status(g:job)) 1554 finally 1555 call job_stop(g:job, 'kill') 1556 unlet g:job 1557 endtry 1558endfunc 1559 1560" This was leaking memory. 1561func Test_partial_in_channel_cycle() 1562 let d = {} 1563 let d.a = function('string', [d]) 1564 try 1565 let d.b = ch_open('nowhere:123', {'close_cb': d.a}) 1566 catch 1567 call assert_exception('E901:') 1568 endtry 1569 unlet d 1570endfunc 1571 1572func Test_using_freed_memory() 1573 let g:a = job_start(['ls']) 1574 sleep 10m 1575 call test_garbagecollect_now() 1576endfunc 1577 1578func Test_collapse_buffers() 1579 if !executable('cat') || !has('job') 1580 return 1581 endif 1582 sp test_channel.vim 1583 let g:linecount = line('$') 1584 close 1585 split testout 1586 1,$delete 1587 call job_start('cat test_channel.vim', {'out_io': 'buffer', 'out_name': 'testout'}) 1588 call WaitFor('line("$") >= g:linecount') 1589 call assert_inrange(g:linecount, g:linecount + 1, line('$')) 1590 bwipe! 1591endfunc 1592 1593func Test_cmd_parsing() 1594 if !has('unix') 1595 return 1596 endif 1597 call assert_false(filereadable("file with space")) 1598 let job = job_start('touch "file with space"') 1599 call WaitFor('filereadable("file with space")') 1600 call assert_true(filereadable("file with space")) 1601 call delete("file with space") 1602 1603 let job = job_start('touch file\ with\ space') 1604 call WaitFor('filereadable("file with space")') 1605 call assert_true(filereadable("file with space")) 1606 call delete("file with space") 1607endfunc 1608 1609func Test_raw_passes_nul() 1610 if !executable('cat') || !has('job') 1611 return 1612 endif 1613 1614 " Test lines from the job containing NUL are stored correctly in a buffer. 1615 new 1616 call setline(1, ["asdf\nasdf", "xxx\n", "\nyyy"]) 1617 w! Xtestread 1618 bwipe! 1619 split testout 1620 1,$delete 1621 call job_start('cat Xtestread', {'out_io': 'buffer', 'out_name': 'testout'}) 1622 call WaitFor('line("$") > 2') 1623 call assert_equal("asdf\nasdf", getline(1)) 1624 call assert_equal("xxx\n", getline(2)) 1625 call assert_equal("\nyyy", getline(3)) 1626 1627 call delete('Xtestread') 1628 bwipe! 1629 1630 " Test lines from a buffer with NUL bytes are written correctly to the job. 1631 new mybuffer 1632 call setline(1, ["asdf\nasdf", "xxx\n", "\nyyy"]) 1633 let g:Ch_job = job_start('cat', {'in_io': 'buffer', 'in_name': 'mybuffer', 'out_io': 'file', 'out_name': 'Xtestwrite'}) 1634 call WaitFor('"dead" == job_status(g:Ch_job)') 1635 bwipe! 1636 split Xtestwrite 1637 call assert_equal("asdf\nasdf", getline(1)) 1638 call assert_equal("xxx\n", getline(2)) 1639 call assert_equal("\nyyy", getline(3)) 1640 1641 call delete('Xtestwrite') 1642 bwipe! 1643endfunc 1644 1645func MyLineCountCb(ch, msg) 1646 let g:linecount += 1 1647endfunc 1648 1649func Test_read_nonl_line() 1650 if !has('job') 1651 return 1652 endif 1653 1654 let g:linecount = 0 1655 let arg = 'import sys;sys.stdout.write("1\n2\n3")' 1656 call job_start([s:python, '-c', arg], {'callback': 'MyLineCountCb'}) 1657 call WaitFor('3 <= g:linecount') 1658 call assert_equal(3, g:linecount) 1659endfunc 1660 1661func Test_read_from_terminated_job() 1662 if !has('job') 1663 return 1664 endif 1665 1666 let g:linecount = 0 1667 let arg = 'import os,sys;os.close(1);sys.stderr.write("test\n")' 1668 call job_start([s:python, '-c', arg], {'callback': 'MyLineCountCb'}) 1669 call WaitFor('1 <= g:linecount') 1670 call assert_equal(1, g:linecount) 1671endfunc 1672 1673func Test_env() 1674 if !has('job') 1675 return 1676 endif 1677 1678 let g:envstr = '' 1679 if has('win32') 1680 call job_start(['cmd', '/c', 'echo %FOO%'], {'callback': {ch,msg->execute(":let g:envstr .= msg")}, 'env':{'FOO': 'bar'}}) 1681 else 1682 call job_start([&shell, &shellcmdflag, 'echo $FOO'], {'callback': {ch,msg->execute(":let g:envstr .= msg")}, 'env':{'FOO': 'bar'}}) 1683 endif 1684 call WaitFor('"" != g:envstr') 1685 call assert_equal("bar", g:envstr) 1686 unlet g:envstr 1687endfunc 1688 1689func Test_cwd() 1690 if !has('job') 1691 return 1692 endif 1693 1694 let g:envstr = '' 1695 if has('win32') 1696 let expect = $TEMP 1697 call job_start(['cmd', '/c', 'echo %CD%'], {'callback': {ch,msg->execute(":let g:envstr .= msg")}, 'cwd': expect}) 1698 else 1699 let expect = $HOME 1700 call job_start(['pwd'], {'callback': {ch,msg->execute(":let g:envstr .= msg")}, 'cwd': expect}) 1701 endif 1702 call WaitFor('"" != g:envstr') 1703 let expect = substitute(expect, '[/\\]$', '', '') 1704 let g:envstr = substitute(g:envstr, '[/\\]$', '', '') 1705 if $CI != '' && stridx(g:envstr, '/private/') == 0 1706 let g:envstr = g:envstr[8:] 1707 endif 1708 call assert_equal(expect, g:envstr) 1709 unlet g:envstr 1710endfunc 1711 1712function Ch_test_close_lambda(port) 1713 let handle = ch_open('localhost:' . a:port, s:chopt) 1714 if ch_status(handle) == "fail" 1715 call assert_report("Can't open channel") 1716 return 1717 endif 1718 let g:Ch_close_ret = '' 1719 call ch_setoptions(handle, {'close_cb': {ch -> execute("let g:Ch_close_ret = 'closed'")}}) 1720 1721 call assert_equal('', ch_evalexpr(handle, 'close me')) 1722 call WaitFor('"closed" == g:Ch_close_ret') 1723 call assert_equal('closed', g:Ch_close_ret) 1724endfunc 1725 1726func Test_close_lambda() 1727 call ch_log('Test_close_lambda()') 1728 call s:run_server('Ch_test_close_lambda') 1729endfunc 1730 1731func s:test_list_args(cmd, out, remove_lf) 1732 try 1733 let g:out = '' 1734 call job_start([s:python, '-c', a:cmd], {'callback': {ch, msg -> execute('let g:out .= msg')}, 'out_mode': 'raw'}) 1735 call WaitFor('"" != g:out') 1736 if has('win32') 1737 let g:out = substitute(g:out, '\r', '', 'g') 1738 endif 1739 if a:remove_lf 1740 let g:out = substitute(g:out, '\n$', '', 'g') 1741 endif 1742 call assert_equal(a:out, g:out) 1743 finally 1744 unlet g:out 1745 endtry 1746endfunc 1747 1748func Test_list_args() 1749 if !has('job') 1750 return 1751 endif 1752 1753 call s:test_list_args('import sys;sys.stdout.write("hello world")', "hello world", 0) 1754 call s:test_list_args('import sys;sys.stdout.write("hello\nworld")', "hello\nworld", 0) 1755 call s:test_list_args('import sys;sys.stdout.write(''hello\nworld'')', "hello\nworld", 0) 1756 call s:test_list_args('import sys;sys.stdout.write(''hello"world'')', "hello\"world", 0) 1757 call s:test_list_args('import sys;sys.stdout.write(''hello^world'')', "hello^world", 0) 1758 call s:test_list_args('import sys;sys.stdout.write("hello&&world")', "hello&&world", 0) 1759 call s:test_list_args('import sys;sys.stdout.write(''hello\\world'')', "hello\\world", 0) 1760 call s:test_list_args('import sys;sys.stdout.write(''hello\\\\world'')', "hello\\\\world", 0) 1761 call s:test_list_args('import sys;sys.stdout.write("hello\"world\"")', 'hello"world"', 0) 1762 call s:test_list_args('import sys;sys.stdout.write("h\"ello worl\"d")', 'h"ello worl"d', 0) 1763 call s:test_list_args('import sys;sys.stdout.write("h\"e\\\"llo wor\\\"l\"d")', 'h"e\"llo wor\"l"d', 0) 1764 call s:test_list_args('import sys;sys.stdout.write("h\"e\\\"llo world")', 'h"e\"llo world', 0) 1765 call s:test_list_args('import sys;sys.stdout.write("hello\tworld")', "hello\tworld", 0) 1766 1767 " tests which not contain spaces in the argument 1768 call s:test_list_args('print("hello\nworld")', "hello\nworld", 1) 1769 call s:test_list_args('print(''hello\nworld'')', "hello\nworld", 1) 1770 call s:test_list_args('print(''hello"world'')', "hello\"world", 1) 1771 call s:test_list_args('print(''hello^world'')', "hello^world", 1) 1772 call s:test_list_args('print("hello&&world")', "hello&&world", 1) 1773 call s:test_list_args('print(''hello\\world'')', "hello\\world", 1) 1774 call s:test_list_args('print(''hello\\\\world'')', "hello\\\\world", 1) 1775 call s:test_list_args('print("hello\"world\"")', 'hello"world"', 1) 1776 call s:test_list_args('print("hello\tworld")', "hello\tworld", 1) 1777endfunc 1778