xref: /vim-8.2.3635/src/testdir/test_channel.vim (revision 4c295027)
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  CheckFeature timers
1352  set cursorline lazyredraw
1353  call test_override('redraw_flag', 1)
1354  new
1355  let g:wipe_buf = bufnr('')
1356
1357  let job = job_start(has('win32') ? 'cmd /c echo:' : ['true'],
1358	\ {'exit_cb': 'ExitCbWipe'})
1359  let timer = timer_start(300, {-> feedkeys("\<Esc>", 'nt')}, {'repeat': 5})
1360  call feedkeys(repeat('g', 1000) . 'o', 'ntx!')
1361  call WaitForAssert({-> assert_equal("dead", job_status(job))})
1362  call timer_stop(timer)
1363
1364  set nocursorline nolazyredraw
1365  unlet g:wipe_buf
1366  call test_override('ALL', 0)
1367endfunc
1368
1369""""""""""
1370
1371let g:Ch_unletResponse = ''
1372func s:UnletHandler(handle, msg)
1373  let g:Ch_unletResponse = a:msg
1374  unlet s:channelfd
1375endfunc
1376
1377" Test that "unlet handle" in a handler doesn't crash Vim.
1378func Ch_unlet_handle(port)
1379  let s:channelfd = ch_open(s:localhost . a:port, s:chopt)
1380  eval s:channelfd->ch_sendexpr("test", {'callback': function('s:UnletHandler')})
1381  call WaitForAssert({-> assert_equal('what?', g:Ch_unletResponse)})
1382endfunc
1383
1384func Test_unlet_handle()
1385  call s:run_server('Ch_unlet_handle')
1386endfunc
1387
1388func Test_unlet_handle_ipv6()
1389  CheckIPv6
1390  call Test_unlet_handle()
1391endfunc
1392
1393""""""""""
1394
1395let g:Ch_unletResponse = ''
1396func Ch_CloseHandler(handle, msg)
1397  let g:Ch_unletResponse = a:msg
1398  eval s:channelfd->ch_close()
1399endfunc
1400
1401" Test that "unlet handle" in a handler doesn't crash Vim.
1402func Ch_close_handle(port)
1403  let s:channelfd = ch_open(s:localhost . a:port, s:chopt)
1404  call ch_sendexpr(s:channelfd, "test", {'callback': function('Ch_CloseHandler')})
1405  call WaitForAssert({-> assert_equal('what?', g:Ch_unletResponse)})
1406endfunc
1407
1408func Test_close_handle()
1409  call s:run_server('Ch_close_handle')
1410endfunc
1411
1412func Test_close_handle_ipv6()
1413  CheckIPv6
1414  call Test_close_handle()
1415endfunc
1416
1417""""""""""
1418
1419func Ch_open_ipv6(port)
1420  let handle = ch_open('[::1]:' .. a:port, s:chopt)
1421  call assert_notequal('fail', ch_status(handle))
1422endfunc
1423
1424func Test_open_ipv6()
1425  CheckIPv6
1426  call s:run_server('Ch_open_ipv6')
1427endfunc
1428
1429""""""""""
1430
1431func Test_open_fail()
1432  call assert_fails("let ch = ch_open('noserver')", 'E475:')
1433  echo ch
1434  let d = ch
1435  call assert_fails("let ch = ch_open('noserver', 10)", 'E474:')
1436  call assert_fails("let ch = ch_open('localhost:-1')", 'E475:')
1437  call assert_fails("let ch = ch_open('localhost:65537')", 'E475:')
1438  call assert_fails("let ch = ch_open('localhost:8765', {'timeout' : -1})",
1439        \ 'E474:')
1440  call assert_fails("let ch = ch_open('localhost:8765', {'axby' : 1})",
1441        \ 'E475:')
1442  call assert_fails("let ch = ch_open('localhost:8765', {'mode' : 'abc'})",
1443        \ 'E475:')
1444  call assert_fails("let ch = ch_open('localhost:8765', {'part' : 'out'})",
1445        \ 'E475:')
1446  call assert_fails("let ch = ch_open('[::]')", 'E475:')
1447  call assert_fails("let ch = ch_open('[::.80')", 'E475:')
1448  call assert_fails("let ch = ch_open('[::]8080')", 'E475:')
1449endfunc
1450
1451func Test_ch_info_fail()
1452  call assert_fails("let x = ch_info(10)", 'E475:')
1453endfunc
1454
1455""""""""""
1456
1457func Ch_open_delay(port)
1458  " Wait up to a second for the port to open.
1459  let s:chopt.waittime = 1000
1460  let channel = ch_open(s:localhost . a:port, s:chopt)
1461  if ch_status(channel) == "fail"
1462    call assert_report("Can't open channel")
1463    return
1464  endif
1465  call assert_equal('got it', channel->ch_evalexpr('hello!'))
1466  call ch_close(channel)
1467endfunc
1468
1469func Test_open_delay()
1470  " The server will wait half a second before creating the port.
1471  call s:run_server('Ch_open_delay', 'delay')
1472endfunc
1473
1474func Test_open_delay_ipv6()
1475  CheckIPv6
1476  call Test_open_delay()
1477endfunc
1478
1479"""""""""
1480
1481function MyFunction(a,b,c)
1482  let g:Ch_call_ret = [a:a, a:b, a:c]
1483endfunc
1484
1485function Ch_test_call(port)
1486  let handle = ch_open(s:localhost . a:port, s:chopt)
1487  if ch_status(handle) == "fail"
1488    call assert_report("Can't open channel")
1489    return
1490  endif
1491
1492  let g:Ch_call_ret = []
1493  call assert_equal('ok', ch_evalexpr(handle, 'call-func'))
1494  call WaitForAssert({-> assert_equal([1, 2, 3], g:Ch_call_ret)})
1495
1496  call assert_fails("let i = ch_evalexpr(handle, '2 + 2', {'callback' : 'abc'})", 'E917:')
1497  call assert_fails("let i = ch_evalexpr(handle, '2 + 2', {'drop' : ''})", 'E475:')
1498  call assert_fails("let i = ch_evalexpr(test_null_job(), '2 + 2')", 'E906:')
1499endfunc
1500
1501func Test_call()
1502  call s:run_server('Ch_test_call')
1503endfunc
1504
1505func Test_call_ipv6()
1506  CheckIPv6
1507  call Test_call()
1508endfunc
1509
1510"""""""""
1511
1512let g:Ch_job_exit_ret = 'not yet'
1513function MyExitCb(job, status)
1514  let g:Ch_job_exit_ret = 'done'
1515endfunc
1516
1517function Ch_test_exit_callback(port)
1518  eval g:currentJob->job_setoptions({'exit_cb': 'MyExitCb'})
1519  let g:Ch_exit_job = g:currentJob
1520  call assert_equal('MyExitCb', job_info(g:currentJob)['exit_cb'])
1521endfunc
1522
1523func Test_exit_callback()
1524  call s:run_server('Ch_test_exit_callback')
1525
1526  " wait up to a second for the job to exit
1527  for i in range(100)
1528    if g:Ch_job_exit_ret == 'done'
1529      break
1530    endif
1531    sleep 10m
1532    " calling job_status() triggers the callback
1533    call job_status(g:Ch_exit_job)
1534  endfor
1535
1536  call assert_equal('done', g:Ch_job_exit_ret)
1537  call assert_equal('dead', job_info(g:Ch_exit_job).status)
1538  unlet g:Ch_exit_job
1539endfunc
1540
1541function MyExitTimeCb(job, status)
1542  if job_info(a:job).process == g:exit_cb_val.process
1543    let g:exit_cb_val.end = reltime(g:exit_cb_val.start)
1544  endif
1545  call Resume()
1546endfunction
1547
1548func Test_exit_callback_interval()
1549  CheckFunction reltimefloat
1550
1551  let g:exit_cb_val = {'start': reltime(), 'end': 0, 'process': 0}
1552  let job = [s:python, '-c', 'import time;time.sleep(0.5)']->job_start({'exit_cb': 'MyExitTimeCb'})
1553  let g:exit_cb_val.process = job_info(job).process
1554  call WaitFor('type(g:exit_cb_val.end) != v:t_number || g:exit_cb_val.end != 0')
1555  let elapsed = reltimefloat(g:exit_cb_val.end)
1556  call assert_true(elapsed > 0.5)
1557  call assert_true(elapsed < 1.0)
1558
1559  " case: unreferenced job, using timer
1560  if !has('timers')
1561    return
1562  endif
1563
1564  let g:exit_cb_val = {'start': reltime(), 'end': 0, 'process': 0}
1565  let g:job = job_start([s:python, '-c', 'import time;time.sleep(0.5)'], {'exit_cb': 'MyExitTimeCb'})
1566  let g:exit_cb_val.process = job_info(g:job).process
1567  unlet g:job
1568  call Standby(1000)
1569  if type(g:exit_cb_val.end) != v:t_number || g:exit_cb_val.end != 0
1570    let elapsed = reltimefloat(g:exit_cb_val.end)
1571  else
1572    let elapsed = 1.0
1573  endif
1574  call assert_inrange(0.5, 1.0, elapsed)
1575endfunc
1576
1577"""""""""
1578
1579let g:Ch_close_ret = 'alive'
1580function MyCloseCb(ch)
1581  let g:Ch_close_ret = 'closed'
1582endfunc
1583
1584function Ch_test_close_callback(port)
1585  let handle = ch_open(s:localhost . a:port, s:chopt)
1586  if ch_status(handle) == "fail"
1587    call assert_report("Can't open channel")
1588    return
1589  endif
1590  call ch_setoptions(handle, {'close_cb': 'MyCloseCb'})
1591
1592  call assert_equal('', ch_evalexpr(handle, 'close me'))
1593  call WaitForAssert({-> assert_equal('closed', g:Ch_close_ret)})
1594endfunc
1595
1596func Test_close_callback()
1597  call s:run_server('Ch_test_close_callback')
1598endfunc
1599
1600func Test_close_callback_ipv6()
1601  CheckIPv6
1602  call Test_close_callback()
1603endfunc
1604
1605function Ch_test_close_partial(port)
1606  let handle = ch_open(s:localhost . a:port, s:chopt)
1607  if ch_status(handle) == "fail"
1608    call assert_report("Can't open channel")
1609    return
1610  endif
1611  let g:Ch_d = {}
1612  func g:Ch_d.closeCb(ch) dict
1613    let self.close_ret = 'closed'
1614  endfunc
1615  call ch_setoptions(handle, {'close_cb': g:Ch_d.closeCb})
1616
1617  call assert_equal('', ch_evalexpr(handle, 'close me'))
1618  call WaitForAssert({-> assert_equal('closed', g:Ch_d.close_ret)})
1619  unlet g:Ch_d
1620endfunc
1621
1622func Test_close_partial()
1623  call s:run_server('Ch_test_close_partial')
1624endfunc
1625
1626func Test_close_partial_ipv6()
1627  CheckIPv6
1628  call Test_close_partial()
1629endfunc
1630
1631func Test_job_start_fails()
1632  " this was leaking memory
1633  call assert_fails("call job_start([''])", "E474:")
1634  call assert_fails('call job_start($x)', 'E474:')
1635  call assert_fails('call job_start("")', 'E474:')
1636  call assert_fails('call job_start("ls", {"out_io" : "abc"})', 'E475:')
1637  call assert_fails('call job_start("ls", {"err_io" : "abc"})', 'E475:')
1638  call assert_fails('call job_start("ls", [])', 'E715:')
1639  call assert_fails("call job_start('ls', {'in_top' : -1})", 'E475:')
1640  call assert_fails("call job_start('ls', {'in_bot' : -1})", 'E475:')
1641  call assert_fails("call job_start('ls', {'channel' : -1})", 'E475:')
1642  call assert_fails("call job_start('ls', {'callback' : -1})", 'E921:')
1643  call assert_fails("call job_start('ls', {'out_cb' : -1})", 'E921:')
1644  call assert_fails("call job_start('ls', {'err_cb' : -1})", 'E921:')
1645  call assert_fails("call job_start('ls', {'close_cb' : -1})", 'E921:')
1646  call assert_fails("call job_start('ls', {'exit_cb' : -1})", 'E921:')
1647  call assert_fails("call job_start('ls', {'term_name' : []})", 'E475:')
1648  call assert_fails("call job_start('ls', {'term_finish' : 'run'})", 'E475:')
1649  call assert_fails("call job_start('ls', {'term_api' : []})", 'E475:')
1650  call assert_fails("call job_start('ls', {'stoponexit' : []})", 'E730:')
1651  call assert_fails("call job_start('ls', {'in_io' : 'file'})", 'E920:')
1652  call assert_fails("call job_start('ls', {'out_io' : 'file'})", 'E920:')
1653  call assert_fails("call job_start('ls', {'err_io' : 'file'})", 'E920:')
1654  call assert_fails("call job_start('ls', {'in_mode' : 'abc'})", 'E475:')
1655  call assert_fails("call job_start('ls', {'out_mode' : 'abc'})", 'E475:')
1656  call assert_fails("call job_start('ls', {'err_mode' : 'abc'})", 'E475:')
1657  call assert_fails("call job_start('ls',
1658        \ {'in_io' : 'buffer', 'in_buf' : 99999})", 'E86:')
1659  call assert_fails("call job_start('ls',
1660        \ {'out_io' : 'buffer', 'out_buf' : 99999})", 'E86:')
1661  call assert_fails("call job_start('ls',
1662        \ {'err_io' : 'buffer', 'err_buf' : 99999})", 'E86:')
1663
1664  call assert_fails("call job_start('ls',
1665        \ {'in_io' : 'buffer', 'in_buf' : -1})", 'E475:')
1666  call assert_fails("call job_start('ls',
1667        \ {'out_io' : 'buffer', 'out_buf' : -1})", 'E475:')
1668  call assert_fails("call job_start('ls',
1669        \ {'err_io' : 'buffer', 'err_buf' : -1})", 'E475:')
1670
1671  set nomodifiable
1672  call assert_fails("call job_start('cmd /c dir',
1673        \ {'out_io' : 'buffer', 'out_buf' :" .. bufnr() .. "})", 'E21:')
1674  call assert_fails("call job_start('cmd /c dir',
1675        \ {'err_io' : 'buffer', 'err_buf' :" .. bufnr() .. "})", 'E21:')
1676  set modifiable
1677
1678  call assert_fails("call job_start('ls', {'in_io' : 'buffer'})", 'E915:')
1679
1680  edit! XXX
1681  let bnum = bufnr()
1682  enew
1683  call assert_fails("call job_start('ls',
1684        \ {'in_io' : 'buffer', 'in_buf' : bnum})", 'E918:')
1685
1686  " Empty job tests
1687  " This was crashing on MS-Windows.
1688  call assert_fails('let job = job_start([""])', 'E474:')
1689  call assert_fails('let job = job_start(["   "])', 'E474:')
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(["ls", []])', 'E730:')
1693  call assert_fails('call job_setoptions(test_null_job(), {})', 'E916:')
1694  %bw!
1695endfunc
1696
1697func Test_job_stop_immediately()
1698  let g:job = job_start([s:python, '-c', 'import time;time.sleep(10)'])
1699  try
1700    eval g:job->job_stop()
1701    call WaitForAssert({-> assert_equal('dead', job_status(g:job))})
1702  finally
1703    call job_stop(g:job, 'kill')
1704    unlet g:job
1705  endtry
1706endfunc
1707
1708" This was leaking memory.
1709func Test_partial_in_channel_cycle()
1710  let d = {}
1711  let d.a = function('string', [d])
1712  try
1713    let d.b = ch_open('nowhere:123', {'close_cb': d.a})
1714    call test_garbagecollect_now()
1715  catch
1716    call assert_exception('E901:')
1717  endtry
1718  unlet d
1719endfunc
1720
1721func Test_using_freed_memory()
1722  let g:a = job_start(['ls'])
1723  sleep 10m
1724  call test_garbagecollect_now()
1725endfunc
1726
1727func Test_collapse_buffers()
1728  CheckExecutable cat
1729
1730  sp test_channel.vim
1731  let g:linecount = line('$')
1732  close
1733  split testout
1734  1,$delete
1735  call job_start('cat test_channel.vim', {'out_io': 'buffer', 'out_name': 'testout'})
1736  call WaitForAssert({-> assert_inrange(g:linecount, g:linecount + 1, line('$'))})
1737  bwipe!
1738endfunc
1739
1740func Test_write_to_deleted_buffer()
1741  CheckExecutable echo
1742  CheckFeature quickfix
1743
1744  let job = job_start('echo hello', {'out_io': 'buffer', 'out_name': 'test_buffer', 'out_msg': 0})
1745  let bufnr = bufnr('test_buffer')
1746  call WaitForAssert({-> assert_equal(['hello'], getbufline(bufnr, 1, '$'))})
1747  call assert_equal('nofile', getbufvar(bufnr, '&buftype'))
1748  call assert_equal('hide', getbufvar(bufnr, '&bufhidden'))
1749
1750  bdel test_buffer
1751  call assert_equal([], getbufline(bufnr, 1, '$'))
1752
1753  let job = job_start('echo hello', {'out_io': 'buffer', 'out_name': 'test_buffer', 'out_msg': 0})
1754  call WaitForAssert({-> assert_equal(['hello'], getbufline(bufnr, 1, '$'))})
1755  call assert_equal('nofile', getbufvar(bufnr, '&buftype'))
1756  call assert_equal('hide', getbufvar(bufnr, '&bufhidden'))
1757
1758  bwipe! test_buffer
1759endfunc
1760
1761func Test_cmd_parsing()
1762  CheckUnix
1763
1764  call assert_false(filereadable("file with space"))
1765  let job = job_start('touch "file with space"')
1766  call WaitForAssert({-> assert_true(filereadable("file with space"))})
1767  call delete("file with space")
1768
1769  let job = job_start('touch file\ with\ space')
1770  call WaitForAssert({-> assert_true(filereadable("file with space"))})
1771  call delete("file with space")
1772endfunc
1773
1774func Test_raw_passes_nul()
1775  CheckExecutable cat
1776
1777  " Test lines from the job containing NUL are stored correctly in a buffer.
1778  new
1779  call setline(1, ["asdf\nasdf", "xxx\n", "\nyyy"])
1780  w! Xtestread
1781  bwipe!
1782  split testout
1783  1,$delete
1784  call job_start('cat Xtestread', {'out_io': 'buffer', 'out_name': 'testout'})
1785  call WaitFor('line("$") > 2')
1786  call assert_equal("asdf\nasdf", getline(1))
1787  call assert_equal("xxx\n", getline(2))
1788  call assert_equal("\nyyy", getline(3))
1789
1790  call delete('Xtestread')
1791  bwipe!
1792
1793  " Test lines from a buffer with NUL bytes are written correctly to the job.
1794  new mybuffer
1795  call setline(1, ["asdf\nasdf", "xxx\n", "\nyyy"])
1796  let g:Ch_job = job_start('cat', {'in_io': 'buffer', 'in_name': 'mybuffer', 'out_io': 'file', 'out_name': 'Xtestwrite'})
1797  call WaitForAssert({-> assert_equal("dead", job_status(g:Ch_job))})
1798  bwipe!
1799  split Xtestwrite
1800  call assert_equal("asdf\nasdf", getline(1))
1801  call assert_equal("xxx\n", getline(2))
1802  call assert_equal("\nyyy", getline(3))
1803  call assert_equal(-1, match(s:get_resources(), '\(^\|/\)Xtestwrite$'))
1804
1805  call delete('Xtestwrite')
1806  bwipe!
1807endfunc
1808
1809func Test_read_nonl_line()
1810  let g:linecount = 0
1811  let arg = 'import sys;sys.stdout.write("1\n2\n3")'
1812  call job_start([s:python, '-c', arg], {'callback': {-> execute('let g:linecount += 1')}})
1813  call WaitForAssert({-> assert_equal(3, g:linecount)})
1814  unlet g:linecount
1815endfunc
1816
1817func Test_read_nonl_in_close_cb()
1818  func s:close_cb(ch)
1819    while ch_status(a:ch) == 'buffered'
1820      let g:out .= ch_read(a:ch)
1821    endwhile
1822  endfunc
1823
1824  let g:out = ''
1825  let arg = 'import sys;sys.stdout.write("1\n2\n3")'
1826  call job_start([s:python, '-c', arg], {'close_cb': function('s:close_cb')})
1827  call test_garbagecollect_now()
1828  call WaitForAssert({-> assert_equal('123', g:out)})
1829  unlet g:out
1830  delfunc s:close_cb
1831endfunc
1832
1833func Test_read_from_terminated_job()
1834  let g:linecount = 0
1835  let arg = 'import os,sys;os.close(1);sys.stderr.write("test\n")'
1836  call job_start([s:python, '-c', arg], {'callback': {-> execute('let g:linecount += 1')}})
1837  call WaitForAssert({-> assert_equal(1, g:linecount)})
1838  call test_garbagecollect_now()
1839  unlet g:linecount
1840endfunc
1841
1842func Test_job_start_windows()
1843  CheckMSWindows
1844
1845  " Check that backslash in $COMSPEC is handled properly.
1846  let g:echostr = ''
1847  let cmd = $COMSPEC . ' /c echo 123'
1848  let job = job_start(cmd, {'callback': {ch,msg -> execute(":let g:echostr .= msg")}})
1849  let info = job_info(job)
1850  call assert_equal([$COMSPEC, '/c', 'echo', '123'], info.cmd)
1851
1852  call WaitForAssert({-> assert_equal("123", g:echostr)})
1853  unlet g:echostr
1854endfunc
1855
1856func Test_env()
1857  let g:envstr = ''
1858  if has('win32')
1859    let cmd = ['cmd', '/c', 'echo %FOO%']
1860  else
1861    let cmd = [&shell, &shellcmdflag, 'echo $FOO']
1862  endif
1863  call assert_fails('call job_start(cmd, {"env": 1})', 'E475:')
1864  call job_start(cmd, {'callback': {ch,msg -> execute(":let g:envstr .= msg")}, 'env': {'FOO': 'bar'}})
1865  call WaitForAssert({-> assert_equal("bar", g:envstr)})
1866  unlet g:envstr
1867endfunc
1868
1869func Test_cwd()
1870  let g:envstr = ''
1871  if has('win32')
1872    let expect = $TEMP
1873    let cmd = ['cmd', '/c', 'echo %CD%']
1874  else
1875    let expect = $HOME
1876    let cmd = ['pwd']
1877  endif
1878  let job = job_start(cmd, {'callback': {ch,msg -> execute(":let g:envstr .= msg")}, 'cwd': expect})
1879  try
1880    call WaitForAssert({-> assert_notequal("", g:envstr)})
1881    let expect = substitute(expect, '[/\\]$', '', '')
1882    let g:envstr = substitute(g:envstr, '[/\\]$', '', '')
1883    if $CI != '' && stridx(g:envstr, '/private/') == 0
1884      let g:envstr = g:envstr[8:]
1885    endif
1886    call assert_equal(expect, g:envstr)
1887  finally
1888    call job_stop(job)
1889    unlet g:envstr
1890  endtry
1891endfunc
1892
1893function Ch_test_close_lambda(port)
1894  let handle = ch_open(s:localhost . a:port, s:chopt)
1895  if ch_status(handle) == "fail"
1896    call assert_report("Can't open channel")
1897    return
1898  endif
1899  let g:Ch_close_ret = ''
1900  call ch_setoptions(handle, {'close_cb': {ch -> execute("let g:Ch_close_ret = 'closed'")}})
1901  call test_garbagecollect_now()
1902
1903  call assert_equal('', ch_evalexpr(handle, 'close me'))
1904  call WaitForAssert({-> assert_equal('closed', g:Ch_close_ret)})
1905endfunc
1906
1907func Test_close_lambda()
1908  call s:run_server('Ch_test_close_lambda')
1909endfunc
1910
1911func Test_close_lambda_ipv6()
1912  CheckIPv6
1913  call Test_close_lambda()
1914endfunc
1915
1916func s:test_list_args(cmd, out, remove_lf)
1917  try
1918    let g:out = ''
1919    let job = job_start([s:python, '-c', a:cmd], {'callback': {ch, msg -> execute('let g:out .= msg')}, 'out_mode': 'raw'})
1920    call WaitFor('"" != g:out')
1921    if has('win32')
1922      let g:out = substitute(g:out, '\r', '', 'g')
1923    endif
1924    if a:remove_lf
1925      let g:out = substitute(g:out, '\n$', '', 'g')
1926    endif
1927    call assert_equal(a:out, g:out)
1928  finally
1929    call job_stop(job)
1930    unlet g:out
1931  endtry
1932endfunc
1933
1934func Test_list_args()
1935  call s:test_list_args('import sys;sys.stdout.write("hello world")', "hello world", 0)
1936  call s:test_list_args('import sys;sys.stdout.write("hello\nworld")', "hello\nworld", 0)
1937  call s:test_list_args('import sys;sys.stdout.write(''hello\nworld'')', "hello\nworld", 0)
1938  call s:test_list_args('import sys;sys.stdout.write(''hello"world'')', "hello\"world", 0)
1939  call s:test_list_args('import sys;sys.stdout.write(''hello^world'')', "hello^world", 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("h\"ello worl\"d")', 'h"ello worl"d', 0)
1945  call s:test_list_args('import sys;sys.stdout.write("h\"e\\\"llo wor\\\"l\"d")', 'h"e\"llo wor\"l"d', 0)
1946  call s:test_list_args('import sys;sys.stdout.write("h\"e\\\"llo world")', 'h"e\"llo world', 0)
1947  call s:test_list_args('import sys;sys.stdout.write("hello\tworld")', "hello\tworld", 0)
1948
1949  " tests which not contain spaces in the argument
1950  call s:test_list_args('print("hello\nworld")', "hello\nworld", 1)
1951  call s:test_list_args('print(''hello\nworld'')', "hello\nworld", 1)
1952  call s:test_list_args('print(''hello"world'')', "hello\"world", 1)
1953  call s:test_list_args('print(''hello^world'')', "hello^world", 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\tworld")', "hello\tworld", 1)
1959endfunc
1960
1961func Test_keep_pty_open()
1962  CheckUnix
1963
1964  let job = job_start(s:python . ' -c "import time;time.sleep(0.2)"',
1965        \ {'out_io': 'null', 'err_io': 'null', 'pty': 1})
1966  let elapsed = WaitFor({-> job_status(job) ==# 'dead'})
1967  call assert_inrange(200, 1000, elapsed)
1968  call job_stop(job)
1969endfunc
1970
1971func Test_job_start_in_timer()
1972  CheckFeature timers
1973  CheckFunction reltimefloat
1974
1975  func OutCb(chan, msg)
1976    let g:val += 1
1977  endfunc
1978
1979  func ExitCb(job, status)
1980    let g:val += 1
1981    call Resume()
1982  endfunc
1983
1984  func TimerCb(timer)
1985    if has('win32')
1986      let cmd = ['cmd', '/c', 'echo.']
1987    else
1988      let cmd = ['echo']
1989    endif
1990    let g:job = job_start(cmd, {'out_cb': 'OutCb', 'exit_cb': 'ExitCb'})
1991    call substitute(repeat('a', 100000), '.', '', 'g')
1992  endfunc
1993
1994  " We should be interrupted before 'updatetime' elapsed.
1995  let g:val = 0
1996  call timer_start(1, 'TimerCb')
1997  let elapsed = Standby(&ut)
1998  call assert_inrange(1, &ut / 2, elapsed)
1999
2000  " Wait for both OutCb() and ExitCb() to have been called before deleting
2001  " them.
2002  call WaitForAssert({-> assert_equal(2, g:val)})
2003  call job_stop(g:job)
2004
2005  delfunc OutCb
2006  delfunc ExitCb
2007  delfunc TimerCb
2008  unlet! g:val
2009  unlet! g:job
2010endfunc
2011
2012func Test_raw_large_data()
2013  try
2014    let g:out = ''
2015    let job = job_start(s:python . " test_channel_pipe.py",
2016          \ {'mode': 'raw', 'drop': 'never', 'noblock': 1,
2017          \  'callback': {ch, msg -> execute('let g:out .= msg')}})
2018
2019    let outlen = 79999
2020    let want = repeat('X', outlen) . "\n"
2021    eval job->ch_sendraw(want)
2022    call WaitFor({-> len(g:out) >= outlen}, 10000)
2023    call WaitForAssert({-> assert_equal("dead", job_status(job))})
2024    call assert_equal(want, substitute(g:out, '\r', '', 'g'))
2025  finally
2026    call job_stop(job)
2027    unlet g:out
2028  endtry
2029endfunc
2030
2031func Test_no_hang_windows()
2032  CheckMSWindows
2033
2034  try
2035    let job = job_start(s:python . " test_channel_pipe.py busy",
2036          \ {'mode': 'raw', 'drop': 'never', 'noblock': 0})
2037    call assert_fails('call ch_sendraw(job, repeat("X", 80000))', 'E631:')
2038  finally
2039    call job_stop(job)
2040  endtry
2041endfunc
2042
2043func Test_job_exitval_and_termsig()
2044  CheckUnix
2045
2046  " Terminate job normally
2047  let cmd = ['echo']
2048  let job = job_start(cmd)
2049  call WaitForAssert({-> assert_equal("dead", job_status(job))})
2050  let info = job_info(job)
2051  call assert_equal(0, info.exitval)
2052  call assert_equal("", info.termsig)
2053
2054  " Terminate job by signal
2055  let cmd = ['sleep', '10']
2056  let job = job_start(cmd)
2057  " 10m usually works but 50m is needed when running Valgrind
2058  sleep 50m
2059  call job_stop(job)
2060  call WaitForAssert({-> assert_equal("dead", job_status(job))})
2061  let info = job_info(job)
2062  call assert_equal(-1, info.exitval)
2063  call assert_equal("term", info.termsig)
2064endfunc
2065
2066func Test_job_tty_in_out()
2067  CheckUnix
2068
2069  call writefile(['test'], 'Xtestin')
2070  let in_opts = [{},
2071        \ {'in_io': 'null'},
2072        \ {'in_io': 'file', 'in_name': 'Xtestin'}]
2073  let out_opts = [{},
2074        \ {'out_io': 'null'},
2075        \ {'out_io': 'file', 'out_name': 'Xtestout'}]
2076  let err_opts = [{},
2077        \ {'err_io': 'null'},
2078        \ {'err_io': 'file', 'err_name': 'Xtesterr'},
2079        \ {'err_io': 'out'}]
2080  let opts = []
2081
2082  for in_opt in in_opts
2083    let x = copy(in_opt)
2084    for out_opt in out_opts
2085      let x = extend(copy(x), out_opt)
2086      for err_opt in err_opts
2087        let x = extend(copy(x), err_opt)
2088        let opts += [extend({'pty': 1}, x)]
2089      endfor
2090    endfor
2091  endfor
2092
2093  for opt in opts
2094    let job = job_start('echo', opt)
2095    let info = job_info(job)
2096    let msg = printf('option={"in_io": "%s", "out_io": "%s", "err_io": "%s"}',
2097          \ get(opt, 'in_io', 'tty'),
2098          \ get(opt, 'out_io', 'tty'),
2099          \ get(opt, 'err_io', 'tty'))
2100
2101    if !has_key(opt, 'in_io') || !has_key(opt, 'out_io') || !has_key(opt, 'err_io')
2102      call assert_notequal('', info.tty_in, msg)
2103    else
2104      call assert_equal('', info.tty_in, msg)
2105    endif
2106    call assert_equal(info.tty_in, info.tty_out, msg)
2107
2108    call WaitForAssert({-> assert_equal('dead', job_status(job))})
2109  endfor
2110
2111  call delete('Xtestin')
2112  call delete('Xtestout')
2113  call delete('Xtesterr')
2114endfunc
2115
2116" Do this last, it stops any channel log.
2117func Test_zz_nl_err_to_out_pipe()
2118
2119  eval 'Xlog'->ch_logfile()
2120  call ch_log('Test_zz_nl_err_to_out_pipe()')
2121  let job = job_start(s:python . " test_channel_pipe.py", {'err_io': 'out'})
2122  call assert_equal("run", job_status(job))
2123  try
2124    let handle = job_getchannel(job)
2125    call ch_sendraw(handle, "echo something\n")
2126    call assert_equal("something", ch_readraw(handle))
2127
2128    call ch_sendraw(handle, "echoerr wrong\n")
2129    call assert_equal("wrong", ch_readraw(handle))
2130  finally
2131    call job_stop(job)
2132    call ch_logfile('')
2133    let loglines = readfile('Xlog')
2134    call assert_true(len(loglines) > 10)
2135    let found_test = 0
2136    let found_send = 0
2137    let found_recv = 0
2138    let found_stop = 0
2139    for l in loglines
2140      if l =~ 'Test_zz_nl_err_to_out_pipe'
2141	let found_test = 1
2142      endif
2143      if l =~ 'SEND on.*echo something'
2144	let found_send = 1
2145      endif
2146      if l =~ 'RECV on.*something'
2147	let found_recv = 1
2148      endif
2149      if l =~ 'Stopping job with'
2150	let found_stop = 1
2151      endif
2152    endfor
2153    call assert_equal(1, found_test)
2154    call assert_equal(1, found_send)
2155    call assert_equal(1, found_recv)
2156    call assert_equal(1, found_stop)
2157    " On MS-Windows need to sleep for a moment to be able to delete the file.
2158    sleep 10m
2159    call delete('Xlog')
2160  endtry
2161endfunc
2162
2163" Do this last, it stops any channel log.
2164func Test_zz_ch_log()
2165  call ch_logfile('Xlog', 'w')
2166  call ch_log('hello there')
2167  call ch_log('%s%s')
2168  call ch_logfile('')
2169  let text = readfile('Xlog')
2170  call assert_match("hello there", text[1])
2171  call assert_match("%s%s", text[2])
2172  call mkdir("Xdir1")
2173  call assert_fails("call ch_logfile('Xdir1')", 'E484:')
2174  cal delete("Xdir1", 'd')
2175  call delete('Xlog')
2176endfunc
2177
2178func Test_issue_5150()
2179  if has('win32')
2180    let cmd = 'cmd /c pause'
2181  else
2182    let cmd = 'grep foo'
2183  endif
2184
2185  let g:job = job_start(cmd, {})
2186  sleep 50m  " give the job time to start
2187  call job_stop(g:job)
2188  call WaitForAssert({-> assert_equal(-1, job_info(g:job).exitval)})
2189
2190  let g:job = job_start(cmd, {})
2191  sleep 50m
2192  call job_stop(g:job, 'term')
2193  call WaitForAssert({-> assert_equal(-1, job_info(g:job).exitval)})
2194
2195  let g:job = job_start(cmd, {})
2196  sleep 50m
2197  call job_stop(g:job, 'kill')
2198  call WaitForAssert({-> assert_equal(-1, job_info(g:job).exitval)})
2199endfunc
2200
2201func Test_issue_5485()
2202  let $VAR1 = 'global'
2203  let g:Ch_reply = ""
2204  let l:job = job_start([&shell, &shellcmdflag, has('win32') ? 'echo %VAR1% %VAR2%' : 'echo $VAR1 $VAR2'], {'env': {'VAR1': 'local', 'VAR2': 'local'}, 'callback': 'Ch_handler'})
2205  let g:Ch_job = l:job
2206  call WaitForAssert({-> assert_equal("local local", trim(g:Ch_reply))})
2207  unlet $VAR1
2208endfunc
2209
2210func Test_job_trailing_space_unix()
2211  CheckUnix
2212  CheckExecutable cat
2213
2214  let job = job_start("cat ", #{in_io: 'null'})
2215  call WaitForAssert({-> assert_equal("dead", job_status(job))})
2216  call assert_equal(0, job_info(job).exitval)
2217endfunc
2218
2219func Test_ch_getbufnr()
2220  let ch = test_null_channel()
2221  call assert_equal(-1, ch_getbufnr(ch, 'in'))
2222  call assert_equal(-1, ch_getbufnr(ch, 'out'))
2223  call assert_equal(-1, ch_getbufnr(ch, 'err'))
2224  call assert_equal(-1, ch_getbufnr(ch, ''))
2225endfunc
2226
2227" Test for unsupported options passed to ch_status()
2228func Test_invalid_job_chan_options()
2229  let ch = test_null_channel()
2230  let invalid_opts = [
2231        \ {'in_io' : 'null'},
2232        \ {'out_io' : 'null'},
2233        \ {'err_io' : 'null'},
2234        \ {'mode' : 'json'},
2235        \ {'out_mode' : 'json'},
2236        \ {'err_mode' : 'json'},
2237        \ {'noblock' : 1},
2238        \ {'in_name' : '/a/b'},
2239        \ {'pty' : 1},
2240        \ {'in_buf' : 1},
2241        \ {'out_buf' : 1},
2242        \ {'err_buf' : 1},
2243        \ {'out_modifiable' : 1},
2244        \ {'err_modifiable' : 1},
2245        \ {'out_msg' : 1},
2246        \ {'err_msg' : 1},
2247        \ {'in_top' : 1},
2248        \ {'in_bot' : 1},
2249        \ {'channel' : ch},
2250        \ {'callback' : ''},
2251        \ {'out_cb' : ''},
2252        \ {'err_cb' : ''},
2253        \ {'close_cb' : ''},
2254        \ {'exit_cb' : ''},
2255        \ {'term_opencmd' : ''},
2256        \ {'eof_chars' : ''},
2257        \ {'term_rows' : 10},
2258        \ {'term_cols' : 10},
2259        \ {'vertical' : 0},
2260        \ {'curwin' : 1},
2261        \ {'bufnr' : 1},
2262        \ {'hidden' : 0},
2263        \ {'norestore' : 0},
2264        \ {'term_kill' : 'kill'},
2265        \ {'tty_type' : ''},
2266        \ {'term_highlight' : ''},
2267        \ {'env' : {}},
2268        \ {'cwd' : ''},
2269        \ {'timeout' : 0},
2270        \ {'out_timeout' : 0},
2271        \ {'err_timeout' : 0},
2272        \ {'id' : 0},
2273        \ {'stoponexit' : ''},
2274        \ {'block_write' : 1}
2275        \ ]
2276  if has('gui')
2277    call add(invalid_opts, {'ansi_colors' : []})
2278  endif
2279
2280  for opt in invalid_opts
2281    call assert_fails("let x = ch_status(ch, opt)", 'E475:')
2282  endfor
2283  call assert_equal('fail', ch_status(ch, test_null_dict()))
2284endfunc
2285
2286" Test for passing the command and the arguments as List on MS-Windows
2287func Test_job_with_list_args()
2288  CheckMSWindows
2289
2290  enew!
2291  let bnum = bufnr()
2292  let job = job_start(['cmd', '/c', 'echo', 'Hello', 'World'], {'out_io' : 'buffer', 'out_buf' : bnum})
2293  call WaitForAssert({-> assert_equal("dead", job_status(job))})
2294  call assert_equal('Hello World', getline(1))
2295  %bw!
2296endfunc
2297
2298func ExitCb_cb_with_input(job, status)
2299  call feedkeys(":\<C-u>echo input('', 'default')\<CR>\<CR>", 'nx')
2300  call assert_equal('default', Screenline(&lines))
2301  let g:wait_exit_cb = 0
2302endfunc
2303
2304func Test_cb_with_input()
2305  let g:wait_exit_cb = 1
2306
2307  if has('win32')
2308    let cmd = 'cmd /c echo "Vim''s test"'
2309  else
2310    let cmd = 'echo "Vim''s test"'
2311  endif
2312
2313  let job = job_start(cmd, {'out_cb': 'ExitCb_cb_with_input'})
2314  call WaitFor({-> job_status(job) == "dead"})
2315  call WaitForAssert({-> assert_equal(0, g:wait_exit_cb)})
2316
2317  unlet g:wait_exit_cb
2318endfunc
2319
2320" vim: shiftwidth=2 sts=2 expandtab
2321