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