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