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