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