neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

shared.vim (10798B)


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