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