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