1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
|
" Author: w0rp <devw0rp@gmail.com>
" Description: Functions for making testing ALE easier.
"
" This file should not typically be loaded during the normal execution of ALE.
" Change the directory for checking things in particular test directories
"
" This function will set the g:dir variable, which represents the working
" directory after changing the path. This variable allows a test to change
" directories, and then switch back to a directory at the start of the test
" run.
"
" This function should be run in a Vader Before: block.
function! ale#test#SetDirectory(docker_path) abort
if a:docker_path[:len('/testplugin/') - 1] isnot# '/testplugin/'
throw 'docker_path must start with /testplugin/!'
endif
" Try to switch directory, which will fail when running tests directly,
" and not through the Docker image.
silent! execute 'cd ' . fnameescape(a:docker_path)
let g:dir = getcwd() " no-custom-checks
endfunction
" When g:dir is defined, switch back to the directory we saved, and then
" delete that variable.
"
" The filename will be reset to dummy.txt
"
" This function should be run in a Vader After: block.
function! ale#test#RestoreDirectory() abort
call ale#test#SetFilename('dummy.txt')
silent execute 'cd ' . fnameescape(g:dir)
unlet! g:dir
endfunction
" Get a filename for the current buffer using a relative path to the script.
"
" If a g:dir variable is set, it will be used as the path to the directory
" containing the test file.
function! ale#test#GetFilename(path) abort
let l:dir = get(g:, 'dir', '')
if empty(l:dir)
let l:dir = getcwd() " no-custom-checks
endif
let l:full_path = ale#path#IsAbsolute(a:path)
\ ? a:path
\ : l:dir . '/' . a:path
return ale#path#Simplify(l:full_path)
endfunction
" Change the filename for the current buffer using a relative path to
" the script without running autocmd commands.
"
" If a g:dir variable is set, it will be used as the path to the directory
" containing the test file.
function! ale#test#SetFilename(path) abort
let l:full_path = ale#test#GetFilename(a:path)
silent! noautocmd execute 'file ' . fnameescape(l:full_path)
endfunction
function! RemoveNewerKeys(results) abort
for l:item in a:results
if has_key(l:item, 'module')
call remove(l:item, 'module')
endif
if has_key(l:item, 'end_col')
call remove(l:item, 'end_col')
endif
if has_key(l:item, 'end_lnum')
call remove(l:item, 'end_lnum')
endif
endfor
endfunction
" Return loclist data with only the keys supported by the lowest Vim versions.
function! ale#test#GetLoclistWithoutNewerKeys() abort
let l:results = getloclist(0)
call RemoveNewerKeys(l:results)
return l:results
endfunction
" Return quickfix data with only the keys supported by the lowest Vim versions.
function! ale#test#GetQflistWithoutNewerKeys() abort
let l:results = getqflist()
call RemoveNewerKeys(l:results)
return l:results
endfunction
function! ale#test#GetPreviewWindowText() abort
for l:window in range(1, winnr('$'))
if getwinvar(l:window, '&previewwindow', 0)
let l:buffer = winbufnr(l:window)
return getbufline(l:buffer, 1, '$')
endif
endfor
endfunction
" This function can be called with a timeout to wait for all jobs to finish.
" If the jobs to not finish in the given number of milliseconds,
" an exception will be thrown.
"
" The time taken will be a very rough approximation, and more time may be
" permitted than is specified.
function! ale#test#WaitForJobs(deadline) abort
let l:start_time = ale#events#ClockMilliseconds()
if l:start_time == 0
throw 'Failed to read milliseconds from the clock!'
endif
let l:job_list = []
" Gather all of the jobs from every buffer.
for [l:buffer, l:data] in items(ale#command#GetData())
call extend(l:job_list, map(keys(l:data.jobs), 'str2nr(v:val)'))
endfor
" NeoVim has a built-in API for this, so use that.
if has('nvim')
let l:nvim_code_list = jobwait(l:job_list, a:deadline)
if index(l:nvim_code_list, -1) >= 0
throw 'Jobs did not complete on time!'
endif
return
endif
let l:should_wait_more = 1
while l:should_wait_more
let l:should_wait_more = 0
for l:job_id in l:job_list
if ale#job#IsRunning(l:job_id)
let l:now = ale#events#ClockMilliseconds()
if l:now - l:start_time > a:deadline
" Stop waiting after a timeout, so we don't wait forever.
throw 'Jobs did not complete on time!'
endif
" Wait another 10 milliseconds
let l:should_wait_more = 1
sleep 10ms
break
endif
endfor
endwhile
" Sleep for a small amount of time after all jobs finish.
" This seems to be enough to let handlers after jobs end run, and
" prevents the occasional failure where this function exits after jobs
" end, but before handlers are run.
sleep 10ms
" We must check the buffer data again to see if new jobs started for
" linters with chained commands.
let l:has_new_jobs = 0
" Check again to see if any jobs are running.
for l:info in values(g:ale_buffer_info)
for [l:job_id, l:linter] in get(l:info, 'job_list', [])
if ale#job#IsRunning(l:job_id)
let l:has_new_jobs = 1
break
endif
endfor
endfor
if l:has_new_jobs
" We have to wait more. Offset the timeout by the time taken so far.
let l:now = ale#events#ClockMilliseconds()
let l:new_deadline = a:deadline - (l:now - l:start_time)
if l:new_deadline <= 0
" Enough time passed already, so stop immediately.
throw 'Jobs did not complete on time!'
endif
call ale#test#WaitForJobs(l:new_deadline)
endif
endfunction
function! ale#test#FlushJobs() abort
" The variable is checked for in a loop, as calling one series of
" callbacks can trigger a further series of callbacks.
while exists('g:ale_run_synchronously_callbacks')
let l:callbacks = g:ale_run_synchronously_callbacks
unlet g:ale_run_synchronously_callbacks
for l:Callback in l:callbacks
call l:Callback()
endfor
endwhile
endfunction
|