xref: /vim-8.2.3635/src/testdir/shared.vim (revision 01a6c216)
1" Functions shared by several tests.
2
3" Only load this script once.
4if exists('*WaitFor')
5  finish
6endif
7
8" Get the name of the Python executable.
9" Also keeps it in s:python.
10func PythonProg()
11  " This test requires the Python command to run the test server.
12  " This most likely only works on Unix and Windows.
13  if has('unix')
14    " We also need the job feature or the pkill command to make sure the server
15    " can be stopped.
16    if !(executable('python') && (has('job') || executable('pkill')))
17      return ''
18    endif
19    let s:python = 'python'
20  elseif has('win32')
21    " Use Python Launcher for Windows (py.exe) if available.
22    if executable('py.exe')
23      let s:python = 'py.exe'
24    elseif executable('python.exe')
25      let s:python = 'python.exe'
26    else
27      return ''
28    endif
29  else
30    return ''
31  endif
32  return s:python
33endfunc
34
35" Run "cmd".  Returns the job if using a job.
36func RunCommand(cmd)
37  let job = 0
38  if has('job')
39    let job = job_start(a:cmd, {"stoponexit": "hup"})
40    call job_setoptions(job, {"stoponexit": "kill"})
41  elseif has('win32')
42    exe 'silent !start cmd /c start "test_channel" ' . a:cmd
43  else
44    exe 'silent !' . a:cmd . '&'
45  endif
46  return job
47endfunc
48
49" Read the port number from the Xportnr file.
50func GetPort()
51  let l = []
52  " with 200 it sometimes failed
53  for i in range(400)
54    try
55      let l = readfile("Xportnr")
56    catch
57    endtry
58    if len(l) >= 1
59      break
60    endif
61    sleep 10m
62  endfor
63  call delete("Xportnr")
64
65  if len(l) == 0
66    " Can't make the connection, give up.
67    return 0
68  endif
69  return l[0]
70endfunc
71
72" Run a Python server for "cmd" and call "testfunc".
73" Always kills the server before returning.
74func RunServer(cmd, testfunc, args)
75  " The Python program writes the port number in Xportnr.
76  call delete("Xportnr")
77
78  if len(a:args) == 1
79    let arg = ' ' . a:args[0]
80  else
81    let arg = ''
82  endif
83  let pycmd = s:python . " " . a:cmd . arg
84
85  try
86    let g:currentJob = RunCommand(pycmd)
87
88    " Wait for up to 2 seconds for the port number to be there.
89    let port = GetPort()
90    if port == 0
91      call assert_false(1, "Can't start " . a:cmd)
92      return
93    endif
94
95    call call(function(a:testfunc), [port])
96  catch
97    call assert_false(1, 'Caught exception: "' . v:exception . '" in ' . v:throwpoint)
98  finally
99    call s:kill_server(a:cmd)
100  endtry
101endfunc
102
103func s:kill_server(cmd)
104  if has('job')
105    if exists('g:currentJob')
106      call job_stop(g:currentJob)
107      unlet g:currentJob
108    endif
109  elseif has('win32')
110    let cmd = substitute(a:cmd, ".py", '', '')
111    call system('taskkill /IM ' . s:python . ' /T /F /FI "WINDOWTITLE eq ' . cmd . '"')
112  else
113    call system("pkill -f " . a:cmd)
114  endif
115endfunc
116
117" Wait for up to five seconds for "expr" to become true.  "expr" can be a
118" stringified expression to evaluate, or a funcref without arguments.
119" Using a lambda works best.  Example:
120"	call WaitFor({-> status == "ok"})
121"
122" A second argument can be used to specify a different timeout in msec.
123"
124" When successful the time slept is returned.
125" When running into the timeout an exception is thrown, thus the function does
126" not return.
127func WaitFor(expr, ...)
128  let timeout = get(a:000, 0, 5000)
129  let slept = s:WaitForCommon(a:expr, v:null, timeout)
130  if slept < 0
131    throw 'WaitFor() timed out after ' . timeout . ' msec'
132  endif
133  return slept
134endfunc
135
136" Wait for up to five seconds for "assert" to return zero.  "assert" must be a
137" (lambda) function containing one assert function.  Example:
138"	call WaitForAssert({-> assert_equal("dead", job_status(job)})
139"
140" A second argument can be used to specify a different timeout in msec.
141"
142" Return zero for success, one for failure (like the assert function).
143func WaitForAssert(assert, ...)
144  let timeout = get(a:000, 0, 5000)
145  if s:WaitForCommon(v:null, a:assert, timeout) < 0
146    return 1
147  endif
148  return 0
149endfunc
150
151" Common implementation of WaitFor() and WaitForAssert().
152" Either "expr" or "assert" is not v:null
153" Return the waiting time for success, -1 for failure.
154func s:WaitForCommon(expr, assert, timeout)
155  " using reltime() is more accurate, but not always available
156  let slept = 0
157  if has('reltime')
158    let start = reltime()
159  endif
160
161  while 1
162    if type(a:expr) == v:t_func
163      let success = a:expr()
164    elseif type(a:assert) == v:t_func
165      let success = a:assert() == 0
166    else
167      let success = eval(a:expr)
168    endif
169    if success
170      return slept
171    endif
172
173    if slept >= a:timeout
174      break
175    endif
176    if type(a:assert) == v:t_func
177      " Remove the error added by the assert function.
178      call remove(v:errors, -1)
179    endif
180
181    sleep 10m
182    if has('reltime')
183      let slept = float2nr(reltimefloat(reltime(start)) * 1000)
184    else
185      let slept += 10
186    endif
187  endwhile
188
189  return -1  " timed out
190endfunc
191
192
193" Wait for up to a given milliseconds.
194" With the +timers feature this waits for key-input by getchar(), Resume()
195" feeds key-input and resumes process. Return time waited in milliseconds.
196" Without +timers it uses simply :sleep.
197func Standby(msec)
198  if has('timers')
199    let start = reltime()
200    let g:_standby_timer = timer_start(a:msec, function('s:feedkeys'))
201    call getchar()
202    return float2nr(reltimefloat(reltime(start)) * 1000)
203  else
204    execute 'sleep ' a:msec . 'm'
205    return a:msec
206  endif
207endfunc
208
209func Resume()
210  if exists('g:_standby_timer')
211    call timer_stop(g:_standby_timer)
212    call s:feedkeys(0)
213    unlet g:_standby_timer
214  endif
215endfunc
216
217func s:feedkeys(timer)
218  call feedkeys('x', 'nt')
219endfunc
220
221" Get $VIMPROG to run Vim executable.
222" The Makefile writes it as the first line in the "vimcmd" file.
223func GetVimProg()
224  if !filereadable('vimcmd')
225    " Assume the script was sourced instead of running "make".
226    return '../vim'
227  endif
228  return readfile('vimcmd')[0]
229endfunc
230
231let g:valgrind_cnt = 1
232
233" Get the command to run Vim, with -u NONE and --not-a-term arguments.
234" If there is an argument use it instead of "NONE".
235func GetVimCommand(...)
236  if !filereadable('vimcmd')
237    echo 'Cannot read the "vimcmd" file, falling back to ../vim.'
238    let lines = ['../vim']
239  else
240    let lines = readfile('vimcmd')
241  endif
242  if a:0 == 0
243    let name = 'NONE'
244  else
245    let name = a:1
246  endif
247  " For Unix Makefile writes the command to use in the second line of the
248  " "vimcmd" file, including environment options.
249  " Other Makefiles just write the executable in the first line, so fall back
250  " to that if there is no second line or it is empty.
251  if len(lines) > 1 && lines[1] != ''
252    let cmd = lines[1]
253  else
254    let cmd = lines[0]
255  endif
256
257  let cmd = substitute(cmd, '-u \f\+', '-u ' . name, '')
258  if cmd !~ '-u '. name
259    let cmd = cmd . ' -u ' . name
260  endif
261  let cmd .= ' --not-a-term'
262  let cmd = substitute(cmd, 'VIMRUNTIME=.*VIMRUNTIME;', '', '')
263
264  " If using valgrind, make sure every run uses a different log file.
265  if cmd =~ 'valgrind.*--log-file='
266    let cmd = substitute(cmd, '--log-file=\(^\s*\)', '--log-file=\1.' . g:valgrind_cnt, '')
267    let g:valgrind_cnt += 1
268  endif
269
270  return cmd
271endfunc
272
273" Get the command to run Vim, with --clean.
274func GetVimCommandClean()
275  let cmd = GetVimCommand()
276  let cmd = substitute(cmd, '-u NONE', '--clean', '')
277  let cmd = substitute(cmd, '--not-a-term', '', '')
278
279  " Optionally run Vim under valgrind
280  " let cmd = 'valgrind --tool=memcheck --leak-check=yes --num-callers=25 --log-file=valgrind ' . cmd
281
282  return cmd
283endfunc
284
285" Run Vim, using the "vimcmd" file and "-u NORC".
286" "before" is a list of Vim commands to be executed before loading plugins.
287" "after" is a list of Vim commands to be executed after loading plugins.
288" Plugins are not loaded, unless 'loadplugins' is set in "before".
289" Return 1 if Vim could be executed.
290func RunVim(before, after, arguments)
291  return RunVimPiped(a:before, a:after, a:arguments, '')
292endfunc
293
294func RunVimPiped(before, after, arguments, pipecmd)
295  let cmd = GetVimCommand()
296  let args = ''
297  if len(a:before) > 0
298    call writefile(a:before, 'Xbefore.vim')
299    let args .= ' --cmd "so Xbefore.vim"'
300  endif
301  if len(a:after) > 0
302    call writefile(a:after, 'Xafter.vim')
303    let args .= ' -S Xafter.vim'
304  endif
305
306  exe "silent !" . a:pipecmd . cmd . args . ' ' . a:arguments
307
308  if len(a:before) > 0
309    call delete('Xbefore.vim')
310  endif
311  if len(a:after) > 0
312    call delete('Xafter.vim')
313  endif
314  return 1
315endfunc
316
317func CanRunGui()
318  return has('gui') && ($DISPLAY != "" || has('gui_running'))
319endfunc
320
321func WorkingClipboard()
322  if !has('clipboard')
323    return 0
324  endif
325  if has('x11')
326    return $DISPLAY != ""
327  endif
328  return 1
329endfunc
330
331" Get line "lnum" as displayed on the screen.
332" Trailing white space is trimmed.
333func! Screenline(lnum)
334  let chars = []
335  for c in range(1, winwidth(0))
336    call add(chars, nr2char(screenchar(a:lnum, c)))
337  endfor
338  let line = join(chars, '')
339  return matchstr(line, '^.\{-}\ze\s*$')
340endfunc
341
342" Stops the shell running in terminal "buf".
343func Stop_shell_in_terminal(buf)
344  call term_sendkeys(a:buf, "exit\r")
345  let job = term_getjob(a:buf)
346  call WaitFor({-> job_status(job) == "dead"})
347endfunc
348
349" Gets the text of a terminal line, using term_scrape()
350func Get_terminal_text(bufnr, row)
351  let list = term_scrape(a:bufnr, a:row)
352  let text = ''
353  for item in list
354    let text .= item.chars
355  endfor
356  return text
357endfunc
358