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 230" Get the command to run Vim, with -u NONE and --not-a-term arguments. 231" If there is an argument use it instead of "NONE". 232func GetVimCommand(...) 233 if !filereadable('vimcmd') 234 echo 'Cannot read the "vimcmd" file, falling back to ../vim.' 235 let lines = ['../vim'] 236 else 237 let lines = readfile('vimcmd') 238 endif 239 if a:0 == 0 240 let name = 'NONE' 241 else 242 let name = a:1 243 endif 244 " For Unix Makefile writes the command to use in the second line of the 245 " "vimcmd" file, including environment options. 246 " Other Makefiles just write the executable in the first line, so fall back 247 " to that if there is no second line. 248 let cmd = get(lines, 1, lines[0]) 249 let cmd = substitute(cmd, '-u \f\+', '-u ' . name, '') 250 if cmd !~ '-u '. name 251 let cmd = cmd . ' -u ' . name 252 endif 253 let cmd .= ' --not-a-term' 254 let cmd = substitute(cmd, 'VIMRUNTIME=.*VIMRUNTIME;', '', '') 255 return cmd 256endfunc 257 258" Get the command to run Vim, with --clean. 259func GetVimCommandClean() 260 let cmd = GetVimCommand() 261 let cmd = substitute(cmd, '-u NONE', '--clean', '') 262 let cmd = substitute(cmd, '--not-a-term', '', '') 263 return cmd 264endfunc 265 266" Run Vim, using the "vimcmd" file and "-u NORC". 267" "before" is a list of Vim commands to be executed before loading plugins. 268" "after" is a list of Vim commands to be executed after loading plugins. 269" Plugins are not loaded, unless 'loadplugins' is set in "before". 270" Return 1 if Vim could be executed. 271func RunVim(before, after, arguments) 272 return RunVimPiped(a:before, a:after, a:arguments, '') 273endfunc 274 275func RunVimPiped(before, after, arguments, pipecmd) 276 let cmd = GetVimCommand() 277 if cmd == '' 278 return 0 279 endif 280 let args = '' 281 if len(a:before) > 0 282 call writefile(a:before, 'Xbefore.vim') 283 let args .= ' --cmd "so Xbefore.vim"' 284 endif 285 if len(a:after) > 0 286 call writefile(a:after, 'Xafter.vim') 287 let args .= ' -S Xafter.vim' 288 endif 289 290 exe "silent !" . a:pipecmd . cmd . args . ' ' . a:arguments 291 292 if len(a:before) > 0 293 call delete('Xbefore.vim') 294 endif 295 if len(a:after) > 0 296 call delete('Xafter.vim') 297 endif 298 return 1 299endfunc 300 301func CanRunGui() 302 return has('gui') && ($DISPLAY != "" || has('gui_running')) 303endfunc 304 305func WorkingClipboard() 306 if !has('clipboard') 307 return 0 308 endif 309 if has('x11') 310 return $DISPLAY != "" 311 endif 312 return 1 313endfunc 314 315" Get line "lnum" as displayed on the screen. 316" Trailing white space is trimmed. 317func! Screenline(lnum) 318 let chars = [] 319 for c in range(1, winwidth(0)) 320 call add(chars, nr2char(screenchar(a:lnum, c))) 321 endfor 322 let line = join(chars, '') 323 return matchstr(line, '^.\{-}\ze\s*$') 324endfunc 325 326" Stops the shell running in terminal "buf". 327func Stop_shell_in_terminal(buf) 328 call term_sendkeys(a:buf, "exit\r") 329 let job = term_getjob(a:buf) 330 call WaitFor({-> job_status(job) == "dead"}) 331endfunc 332