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 five seconds for "expr" to become true. "expr" can be a 117" stringified expression to evaluate, or a funcref without arguments. 118" Using a lambda works best. Example: 119" call WaitFor({-> status == "ok"}) 120" 121" A second argument can be used to specify a different timeout in msec. 122" 123" When successful the time slept is returned. 124" When running into the timeout an exception is thrown, thus the function does 125" not return. 126func WaitFor(expr, ...) 127 let timeout = get(a:000, 0, 5000) 128 let slept = s:WaitForCommon(a:expr, v:null, timeout) 129 if slept < 0 130 throw 'WaitFor() timed out after ' . timeout . ' msec' 131 endif 132 return slept 133endfunc 134 135" Wait for up to five seconds for "assert" to return zero. "assert" must be a 136" (lambda) function containing one assert function. Example: 137" call WaitForAssert({-> assert_equal("dead", job_status(job)}) 138" 139" A second argument can be used to specify a different timeout in msec. 140" 141" Return zero for success, one for failure (like the assert function). 142func WaitForAssert(assert, ...) 143 let timeout = get(a:000, 0, 5000) 144 if s:WaitForCommon(v:null, a:assert, timeout) < 0 145 return 1 146 endif 147 return 0 148endfunc 149 150" Common implementation of WaitFor() and WaitForAssert(). 151" Either "expr" or "assert" is not v:null 152" Return the waiting time for success, -1 for failure. 153func s:WaitForCommon(expr, assert, timeout) 154 " using reltime() is more accurate, but not always available 155 let slept = 0 156 if has('reltime') 157 let start = reltime() 158 endif 159 160 while 1 161 if type(a:expr) == v:t_func 162 let success = a:expr() 163 elseif type(a:assert) == v:t_func 164 let success = a:assert() == 0 165 else 166 let success = eval(a:expr) 167 endif 168 if success 169 return slept 170 endif 171 172 if slept >= a:timeout 173 break 174 endif 175 if type(a:assert) == v:t_func 176 " Remove the error added by the assert function. 177 call remove(v:errors, -1) 178 endif 179 180 sleep 10m 181 if has('reltime') 182 let slept = float2nr(reltimefloat(reltime(start)) * 1000) 183 else 184 let slept += 10 185 endif 186 endwhile 187 188 return -1 " timed out 189endfunc 190 191 192" Wait for up to a given milliseconds. 193" With the +timers feature this waits for key-input by getchar(), Resume() 194" feeds key-input and resumes process. Return time waited in milliseconds. 195" Without +timers it uses simply :sleep. 196func Standby(msec) 197 if has('timers') 198 let start = reltime() 199 let g:_standby_timer = timer_start(a:msec, function('s:feedkeys')) 200 call getchar() 201 return float2nr(reltimefloat(reltime(start)) * 1000) 202 else 203 execute 'sleep ' a:msec . 'm' 204 return a:msec 205 endif 206endfunc 207 208func Resume() 209 if exists('g:_standby_timer') 210 call timer_stop(g:_standby_timer) 211 call s:feedkeys(0) 212 unlet g:_standby_timer 213 endif 214endfunc 215 216func s:feedkeys(timer) 217 call feedkeys('x', 'nt') 218endfunc 219 220" Get $VIMPROG to run Vim executable. 221" The Makefile writes it as the first line in the "vimcmd" file. 222func GetVimProg() 223 if !filereadable('vimcmd') 224 " Assume the script was sourced instead of running "make". 225 return '../vim' 226 endif 227 return readfile('vimcmd')[0] 228endfunc 229 230let g:valgrind_cnt = 1 231 232" Get the command to run Vim, with -u NONE and --not-a-term arguments. 233" If there is an argument use it instead of "NONE". 234func GetVimCommand(...) 235 if !filereadable('vimcmd') 236 echo 'Cannot read the "vimcmd" file, falling back to ../vim.' 237 let lines = ['../vim'] 238 else 239 let lines = readfile('vimcmd') 240 endif 241 if a:0 == 0 242 let name = 'NONE' 243 else 244 let name = a:1 245 endif 246 " For Unix Makefile writes the command to use in the second line of the 247 " "vimcmd" file, including environment options. 248 " Other Makefiles just write the executable in the first line, so fall back 249 " to that if there is no second line or it is empty. 250 if len(lines) > 1 && lines[1] != '' 251 let cmd = lines[1] 252 else 253 let cmd = lines[0] 254 endif 255 256 let cmd = substitute(cmd, '-u \f\+', '-u ' . name, '') 257 if cmd !~ '-u '. name 258 let cmd = cmd . ' -u ' . name 259 endif 260 let cmd .= ' --not-a-term' 261 let cmd = substitute(cmd, 'VIMRUNTIME=.*VIMRUNTIME;', '', '') 262 263 " If using valgrind, make sure every run uses a different log file. 264 if cmd =~ 'valgrind.*--log-file=' 265 let cmd = substitute(cmd, '--log-file=\(^\s*\)', '--log-file=\1.' . g:valgrind_cnt, '') 266 let g:valgrind_cnt += 1 267 endif 268 269 return cmd 270endfunc 271 272" Get the command to run Vim, with --clean. 273func GetVimCommandClean() 274 let cmd = GetVimCommand() 275 let cmd = substitute(cmd, '-u NONE', '--clean', '') 276 let cmd = substitute(cmd, '--not-a-term', '', '') 277 return cmd 278endfunc 279 280" Run Vim, using the "vimcmd" file and "-u NORC". 281" "before" is a list of Vim commands to be executed before loading plugins. 282" "after" is a list of Vim commands to be executed after loading plugins. 283" Plugins are not loaded, unless 'loadplugins' is set in "before". 284" Return 1 if Vim could be executed. 285func RunVim(before, after, arguments) 286 return RunVimPiped(a:before, a:after, a:arguments, '') 287endfunc 288 289func RunVimPiped(before, after, arguments, pipecmd) 290 let cmd = GetVimCommand() 291 let args = '' 292 if len(a:before) > 0 293 call writefile(a:before, 'Xbefore.vim') 294 let args .= ' --cmd "so Xbefore.vim"' 295 endif 296 if len(a:after) > 0 297 call writefile(a:after, 'Xafter.vim') 298 let args .= ' -S Xafter.vim' 299 endif 300 301 exe "silent !" . a:pipecmd . cmd . args . ' ' . a:arguments 302 303 if len(a:before) > 0 304 call delete('Xbefore.vim') 305 endif 306 if len(a:after) > 0 307 call delete('Xafter.vim') 308 endif 309 return 1 310endfunc 311 312func CanRunGui() 313 return has('gui') && ($DISPLAY != "" || has('gui_running')) 314endfunc 315 316func WorkingClipboard() 317 if !has('clipboard') 318 return 0 319 endif 320 if has('x11') 321 return $DISPLAY != "" 322 endif 323 return 1 324endfunc 325 326" Get line "lnum" as displayed on the screen. 327" Trailing white space is trimmed. 328func! Screenline(lnum) 329 let chars = [] 330 for c in range(1, winwidth(0)) 331 call add(chars, nr2char(screenchar(a:lnum, c))) 332 endfor 333 let line = join(chars, '') 334 return matchstr(line, '^.\{-}\ze\s*$') 335endfunc 336 337" Stops the shell running in terminal "buf". 338func Stop_shell_in_terminal(buf) 339 call term_sendkeys(a:buf, "exit\r") 340 let job = term_getjob(a:buf) 341 call WaitFor({-> job_status(job) == "dead"}) 342endfunc 343