shared.vim 11 KB

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