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" Run the list of commands in 'cmds' and look for 'errstr' in exception. 357" Note that assert_fails() cannot be used in some places and this function 358" can be used. 359func AssertException(cmds, errstr) 360 let save_exception = '' 361 try 362 for cmd in a:cmds 363 exe cmd 364 endfor 365 catch 366 let save_exception = v:exception 367 endtry 368 call assert_match(a:errstr, save_exception) 369endfunc 370 371" vim: shiftwidth=2 sts=2 expandtab 372