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 exists('*reltimefloat') 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 exists('*reltimefloat') 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') && exists('*reltimefloat') 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 " Optionally run Vim under valgrind 319 " let cmd = 'valgrind --tool=memcheck --leak-check=yes --num-callers=25 --log-file=valgrind ' . cmd 320 321 exe "silent !" . a:pipecmd . cmd . args . ' ' . a:arguments 322 323 if len(a:before) > 0 324 call delete('Xbefore.vim') 325 endif 326 if len(a:after) > 0 327 call delete('Xafter.vim') 328 endif 329 return 1 330endfunc 331 332func IsRoot() 333 if !has('unix') 334 return v:false 335 elseif $USER == 'root' || system('id -un') =~ '\<root\>' 336 return v:true 337 endif 338 return v:false 339endfunc 340 341" vim: shiftwidth=2 sts=2 expandtab 342