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