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 " with 200 it sometimes failed 53 for i in range(400) 54 try 55 let l = readfile("Xportnr") 56 catch 57 endtry 58 if len(l) >= 1 59 break 60 endif 61 sleep 10m 62 endfor 63 call delete("Xportnr") 64 65 if len(l) == 0 66 " Can't make the connection, give up. 67 return 0 68 endif 69 return l[0] 70endfunc 71 72" Run a Python server for "cmd" and call "testfunc". 73" Always kills the server before returning. 74func RunServer(cmd, testfunc, args) 75 " The Python program writes the port number in Xportnr. 76 call delete("Xportnr") 77 78 if len(a:args) == 1 79 let arg = ' ' . a:args[0] 80 else 81 let arg = '' 82 endif 83 let pycmd = s:python . " " . a:cmd . arg 84 85 try 86 let g:currentJob = RunCommand(pycmd) 87 88 " Wait for up to 2 seconds for the port number to be there. 89 let port = GetPort() 90 if port == 0 91 call assert_false(1, "Can't start " . a:cmd) 92 return 93 endif 94 95 call call(function(a:testfunc), [port]) 96 catch 97 call assert_false(1, 'Caught exception: "' . v:exception . '" in ' . v:throwpoint) 98 finally 99 call s:kill_server(a:cmd) 100 endtry 101endfunc 102 103func s:kill_server(cmd) 104 if has('job') 105 if exists('g:currentJob') 106 call job_stop(g:currentJob) 107 unlet g:currentJob 108 endif 109 elseif has('win32') 110 let cmd = substitute(a:cmd, ".py", '', '') 111 call system('taskkill /IM ' . s:python . ' /T /F /FI "WINDOWTITLE eq ' . cmd . '"') 112 else 113 call system("pkill -f " . a:cmd) 114 endif 115endfunc 116 117" Wait for up to five seconds for "expr" to become true. "expr" can be a 118" stringified expression to evaluate, or a funcref without arguments. 119" Using a lambda works best. Example: 120" call WaitFor({-> status == "ok"}) 121" 122" A second argument can be used to specify a different timeout in msec. 123" 124" When successful the time slept is returned. 125" When running into the timeout an exception is thrown, thus the function does 126" not return. 127func WaitFor(expr, ...) 128 let timeout = get(a:000, 0, 5000) 129 let slept = s:WaitForCommon(a:expr, v:null, timeout) 130 if slept < 0 131 throw 'WaitFor() timed out after ' . timeout . ' msec' 132 endif 133 return slept 134endfunc 135 136" Wait for up to five seconds for "assert" to return zero. "assert" must be a 137" (lambda) function containing one assert function. Example: 138" call WaitForAssert({-> assert_equal("dead", job_status(job)}) 139" 140" A second argument can be used to specify a different timeout in msec. 141" 142" Return zero for success, one for failure (like the assert function). 143func WaitForAssert(assert, ...) 144 let timeout = get(a:000, 0, 5000) 145 if s:WaitForCommon(v:null, a:assert, timeout) < 0 146 return 1 147 endif 148 return 0 149endfunc 150 151" Common implementation of WaitFor() and WaitForAssert(). 152" Either "expr" or "assert" is not v:null 153" Return the waiting time for success, -1 for failure. 154func s:WaitForCommon(expr, assert, timeout) 155 " using reltime() is more accurate, but not always available 156 let slept = 0 157 if has('reltime') 158 let start = reltime() 159 endif 160 161 while 1 162 if type(a:expr) == v:t_func 163 let success = a:expr() 164 elseif type(a:assert) == v:t_func 165 let success = a:assert() == 0 166 else 167 let success = eval(a:expr) 168 endif 169 if success 170 return slept 171 endif 172 173 if slept >= a:timeout 174 break 175 endif 176 if type(a:assert) == v:t_func 177 " Remove the error added by the assert function. 178 call remove(v:errors, -1) 179 endif 180 181 sleep 10m 182 if has('reltime') 183 let slept = float2nr(reltimefloat(reltime(start)) * 1000) 184 else 185 let slept += 10 186 endif 187 endwhile 188 189 return -1 " timed out 190endfunc 191 192 193" Wait for up to a given milliseconds. 194" With the +timers feature this waits for key-input by getchar(), Resume() 195" feeds key-input and resumes process. Return time waited in milliseconds. 196" Without +timers it uses simply :sleep. 197func Standby(msec) 198 if has('timers') 199 let start = reltime() 200 let g:_standby_timer = timer_start(a:msec, function('s:feedkeys')) 201 call getchar() 202 return float2nr(reltimefloat(reltime(start)) * 1000) 203 else 204 execute 'sleep ' a:msec . 'm' 205 return a:msec 206 endif 207endfunc 208 209func Resume() 210 if exists('g:_standby_timer') 211 call timer_stop(g:_standby_timer) 212 call s:feedkeys(0) 213 unlet g:_standby_timer 214 endif 215endfunc 216 217func s:feedkeys(timer) 218 call feedkeys('x', 'nt') 219endfunc 220 221" Get $VIMPROG to run Vim executable. 222" The Makefile writes it as the first line in the "vimcmd" file. 223func GetVimProg() 224 if !filereadable('vimcmd') 225 " Assume the script was sourced instead of running "make". 226 return '../vim' 227 endif 228 return readfile('vimcmd')[0] 229endfunc 230 231let g:valgrind_cnt = 1 232 233" Get the command to run Vim, with -u NONE and --not-a-term arguments. 234" If there is an argument use it instead of "NONE". 235func GetVimCommand(...) 236 if !filereadable('vimcmd') 237 echo 'Cannot read the "vimcmd" file, falling back to ../vim.' 238 let lines = ['../vim'] 239 else 240 let lines = readfile('vimcmd') 241 endif 242 if a:0 == 0 243 let name = 'NONE' 244 else 245 let name = a:1 246 endif 247 " For Unix Makefile writes the command to use in the second line of the 248 " "vimcmd" file, including environment options. 249 " Other Makefiles just write the executable in the first line, so fall back 250 " to that if there is no second line or it is empty. 251 if len(lines) > 1 && lines[1] != '' 252 let cmd = lines[1] 253 else 254 let cmd = lines[0] 255 endif 256 257 let cmd = substitute(cmd, '-u \f\+', '-u ' . name, '') 258 if cmd !~ '-u '. name 259 let cmd = cmd . ' -u ' . name 260 endif 261 let cmd .= ' --not-a-term' 262 let cmd = substitute(cmd, 'VIMRUNTIME=.*VIMRUNTIME;', '', '') 263 264 " If using valgrind, make sure every run uses a different log file. 265 if cmd =~ 'valgrind.*--log-file=' 266 let cmd = substitute(cmd, '--log-file=\(^\s*\)', '--log-file=\1.' . g:valgrind_cnt, '') 267 let g:valgrind_cnt += 1 268 endif 269 270 return cmd 271endfunc 272 273" Get the command to run Vim, with --clean. 274func GetVimCommandClean() 275 let cmd = GetVimCommand() 276 let cmd = substitute(cmd, '-u NONE', '--clean', '') 277 let cmd = substitute(cmd, '--not-a-term', '', '') 278 279 " Optionally run Vim under valgrind 280 " let cmd = 'valgrind --tool=memcheck --leak-check=yes --num-callers=25 --log-file=valgrind ' . cmd 281 282 return cmd 283endfunc 284 285" Run Vim, using the "vimcmd" file and "-u NORC". 286" "before" is a list of Vim commands to be executed before loading plugins. 287" "after" is a list of Vim commands to be executed after loading plugins. 288" Plugins are not loaded, unless 'loadplugins' is set in "before". 289" Return 1 if Vim could be executed. 290func RunVim(before, after, arguments) 291 return RunVimPiped(a:before, a:after, a:arguments, '') 292endfunc 293 294func RunVimPiped(before, after, arguments, pipecmd) 295 let cmd = GetVimCommand() 296 let args = '' 297 if len(a:before) > 0 298 call writefile(a:before, 'Xbefore.vim') 299 let args .= ' --cmd "so Xbefore.vim"' 300 endif 301 if len(a:after) > 0 302 call writefile(a:after, 'Xafter.vim') 303 let args .= ' -S Xafter.vim' 304 endif 305 306 exe "silent !" . a:pipecmd . cmd . args . ' ' . a:arguments 307 308 if len(a:before) > 0 309 call delete('Xbefore.vim') 310 endif 311 if len(a:after) > 0 312 call delete('Xafter.vim') 313 endif 314 return 1 315endfunc 316 317func CanRunGui() 318 return has('gui') && ($DISPLAY != "" || has('gui_running')) 319endfunc 320 321func WorkingClipboard() 322 if !has('clipboard') 323 return 0 324 endif 325 if has('x11') 326 return $DISPLAY != "" 327 endif 328 return 1 329endfunc 330 331" Get line "lnum" as displayed on the screen. 332" Trailing white space is trimmed. 333func! Screenline(lnum) 334 let chars = [] 335 for c in range(1, winwidth(0)) 336 call add(chars, nr2char(screenchar(a:lnum, c))) 337 endfor 338 let line = join(chars, '') 339 return matchstr(line, '^.\{-}\ze\s*$') 340endfunc 341 342" Stops the shell running in terminal "buf". 343func Stop_shell_in_terminal(buf) 344 call term_sendkeys(a:buf, "exit\r") 345 let job = term_getjob(a:buf) 346 call WaitFor({-> job_status(job) == "dead"}) 347endfunc 348 349" Gets the text of a terminal line, using term_scrape() 350func Get_terminal_text(bufnr, row) 351 let list = term_scrape(a:bufnr, a:row) 352 let text = '' 353 for item in list 354 let text .= item.chars 355 endfor 356 return text 357endfunc 358