xref: /vim-8.2.3635/src/testdir/shared.vim (revision 200d0e36)
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 a second for "expr" to become true.  "expr" can be a
117" stringified expression to evaluate, or a funcref without arguments.
118"
119" Return time slept in milliseconds.  With the +reltime feature this can be
120" more than the actual waiting time.  Without +reltime it can also be less.
121func WaitFor(expr, ...)
122  let timeout = get(a:000, 0, 1000)
123  " using reltime() is more accurate, but not always available
124  if has('reltime')
125    let start = reltime()
126  else
127    let slept = 0
128  endif
129  if type(a:expr) == v:t_func
130    let Test = a:expr
131  else
132    let Test = {-> eval(a:expr) }
133  endif
134  for i in range(timeout / 10)
135    if Test()
136      if has('reltime')
137	return float2nr(reltimefloat(reltime(start)) * 1000)
138      endif
139      return slept
140    endif
141    if !has('reltime')
142      let slept += 10
143    endif
144    sleep 10m
145  endfor
146  throw 'WaitFor() timed out after ' . timeout . ' msec'
147endfunc
148
149" Wait for up to a given milliseconds.
150" With the +timers feature this waits for key-input by getchar(), Resume()
151" feeds key-input and resumes process. Return time waited in milliseconds.
152" Without +timers it uses simply :sleep.
153func Standby(msec)
154  if has('timers')
155    let start = reltime()
156    let g:_standby_timer = timer_start(a:msec, function('s:feedkeys'))
157    call getchar()
158    return float2nr(reltimefloat(reltime(start)) * 1000)
159  else
160    execute 'sleep ' a:msec . 'm'
161    return a:msec
162  endif
163endfunc
164
165func Resume()
166  if exists('g:_standby_timer')
167    call timer_stop(g:_standby_timer)
168    call s:feedkeys(0)
169    unlet g:_standby_timer
170  endif
171endfunc
172
173func s:feedkeys(timer)
174  call feedkeys('x', 'nt')
175endfunc
176
177" Get $VIMPROG to run Vim executable.
178" The Makefile writes it as the first line in the "vimcmd" file.
179func GetVimProg()
180  if !filereadable('vimcmd')
181    return ''
182  endif
183  return readfile('vimcmd')[0]
184endfunc
185
186" Get the command to run Vim, with -u NONE and --not-a-term arguments.
187" If there is an argument use it instead of "NONE".
188" Returns an empty string on error.
189func GetVimCommand(...)
190  if !filereadable('vimcmd')
191    return ''
192  endif
193  if a:0 == 0
194    let name = 'NONE'
195  else
196    let name = a:1
197  endif
198  " For Unix Makefile writes the command to use in the second line of the
199  " "vimcmd" file, including environment options.
200  " Other Makefiles just write the executable in the first line, so fall back
201  " to that if there is no second line.
202  let lines = readfile('vimcmd')
203  let cmd = get(lines, 1, lines[0])
204  let cmd = substitute(cmd, '-u \f\+', '-u ' . name, '')
205  if cmd !~ '-u '. name
206    let cmd = cmd . ' -u ' . name
207  endif
208  let cmd .= ' --not-a-term'
209  let cmd = substitute(cmd, 'VIMRUNTIME=.*VIMRUNTIME;', '', '')
210  return cmd
211endfunc
212
213" Run Vim, using the "vimcmd" file and "-u NORC".
214" "before" is a list of Vim commands to be executed before loading plugins.
215" "after" is a list of Vim commands to be executed after loading plugins.
216" Plugins are not loaded, unless 'loadplugins' is set in "before".
217" Return 1 if Vim could be executed.
218func RunVim(before, after, arguments)
219  return RunVimPiped(a:before, a:after, a:arguments, '')
220endfunc
221
222func RunVimPiped(before, after, arguments, pipecmd)
223  let cmd = GetVimCommand()
224  if cmd == ''
225    return 0
226  endif
227  let args = ''
228  if len(a:before) > 0
229    call writefile(a:before, 'Xbefore.vim')
230    let args .= ' --cmd "so Xbefore.vim"'
231  endif
232  if len(a:after) > 0
233    call writefile(a:after, 'Xafter.vim')
234    let args .= ' -S Xafter.vim'
235  endif
236
237  exe "silent !" . a:pipecmd . cmd . args . ' ' . a:arguments
238
239  if len(a:before) > 0
240    call delete('Xbefore.vim')
241  endif
242  if len(a:after) > 0
243    call delete('Xafter.vim')
244  endif
245  return 1
246endfunc
247
248func CanRunGui()
249  return has('gui') && ($DISPLAY != "" || has('gui_running'))
250endfunc
251