All patches and comments are welcome. Please squash your changes to logical
commits before using git-format-patch and git-send-email to
patches@git.madduck.net.
If you'd read over the Git project's submission guidelines and adhered to them,
I'd be especially grateful.
1 " Author: w0rp <devw0rp@gmail.com>
2 " Description: Functions for making testing ALE easier.
4 " This file should not typically be loaded during the normal execution of ALE.
6 " Change the directory for checking things in particular test directories
8 " This function will set the g:dir variable, which represents the working
9 " directory after changing the path. This variable allows a test to change
10 " directories, and then switch back to a directory at the start of the test
13 " This function should be run in a Vader Before: block.
14 function! ale#test#SetDirectory(docker_path) abort
15 if a:docker_path[:len('/testplugin/') - 1] isnot# '/testplugin/'
16 throw 'docker_path must start with /testplugin/!'
19 " Try to switch directory, which will fail when running tests directly,
20 " and not through the Docker image.
21 silent! execute 'cd ' . fnameescape(a:docker_path)
22 let g:dir = getcwd() " no-custom-checks
25 " When g:dir is defined, switch back to the directory we saved, and then
26 " delete that variable.
28 " The filename will be reset to dummy.txt
30 " This function should be run in a Vader After: block.
31 function! ale#test#RestoreDirectory() abort
32 call ale#test#SetFilename('dummy.txt')
33 silent execute 'cd ' . fnameescape(g:dir)
37 " Get a filename for the current buffer using a relative path to the script.
39 " If a g:dir variable is set, it will be used as the path to the directory
40 " containing the test file.
41 function! ale#test#GetFilename(path) abort
42 let l:dir = get(g:, 'dir', '')
45 let l:dir = getcwd() " no-custom-checks
48 let l:full_path = ale#path#IsAbsolute(a:path)
50 \ : l:dir . '/' . a:path
52 return ale#path#Simplify(l:full_path)
55 " Change the filename for the current buffer using a relative path to
56 " the script without running autocmd commands.
58 " If a g:dir variable is set, it will be used as the path to the directory
59 " containing the test file.
60 function! ale#test#SetFilename(path) abort
61 let l:full_path = ale#test#GetFilename(a:path)
62 silent! noautocmd execute 'file ' . fnameescape(l:full_path)
65 function! RemoveNewerKeys(results) abort
66 for l:item in a:results
67 if has_key(l:item, 'module')
68 call remove(l:item, 'module')
71 if has_key(l:item, 'end_col')
72 call remove(l:item, 'end_col')
75 if has_key(l:item, 'end_lnum')
76 call remove(l:item, 'end_lnum')
81 " Return loclist data with only the keys supported by the lowest Vim versions.
82 function! ale#test#GetLoclistWithoutNewerKeys() abort
83 let l:results = getloclist(0)
84 call RemoveNewerKeys(l:results)
89 " Return quickfix data with only the keys supported by the lowest Vim versions.
90 function! ale#test#GetQflistWithoutNewerKeys() abort
91 let l:results = getqflist()
92 call RemoveNewerKeys(l:results)
97 function! ale#test#GetPreviewWindowText() abort
98 for l:window in range(1, winnr('$'))
99 if getwinvar(l:window, '&previewwindow', 0)
100 let l:buffer = winbufnr(l:window)
102 return getbufline(l:buffer, 1, '$')
107 " This function can be called with a timeout to wait for all jobs to finish.
108 " If the jobs to not finish in the given number of milliseconds,
109 " an exception will be thrown.
111 " The time taken will be a very rough approximation, and more time may be
112 " permitted than is specified.
113 function! ale#test#WaitForJobs(deadline) abort
114 let l:start_time = ale#events#ClockMilliseconds()
117 throw 'Failed to read milliseconds from the clock!'
122 " Gather all of the jobs from every buffer.
123 for [l:buffer, l:data] in items(ale#command#GetData())
124 call extend(l:job_list, map(keys(l:data.jobs), 'str2nr(v:val)'))
127 " NeoVim has a built-in API for this, so use that.
129 let l:nvim_code_list = jobwait(l:job_list, a:deadline)
131 if index(l:nvim_code_list, -1) >= 0
132 throw 'Jobs did not complete on time!'
138 let l:should_wait_more = 1
140 while l:should_wait_more
141 let l:should_wait_more = 0
143 for l:job_id in l:job_list
144 if ale#job#IsRunning(l:job_id)
145 let l:now = ale#events#ClockMilliseconds()
147 if l:now - l:start_time > a:deadline
148 " Stop waiting after a timeout, so we don't wait forever.
149 throw 'Jobs did not complete on time!'
152 " Wait another 10 milliseconds
153 let l:should_wait_more = 1
160 " Sleep for a small amount of time after all jobs finish.
161 " This seems to be enough to let handlers after jobs end run, and
162 " prevents the occasional failure where this function exits after jobs
163 " end, but before handlers are run.
166 " We must check the buffer data again to see if new jobs started for
167 " linters with chained commands.
168 let l:has_new_jobs = 0
170 " Check again to see if any jobs are running.
171 for l:info in values(g:ale_buffer_info)
172 for [l:job_id, l:linter] in get(l:info, 'job_list', [])
173 if ale#job#IsRunning(l:job_id)
174 let l:has_new_jobs = 1
181 " We have to wait more. Offset the timeout by the time taken so far.
182 let l:now = ale#events#ClockMilliseconds()
183 let l:new_deadline = a:deadline - (l:now - l:start_time)
185 if l:new_deadline <= 0
186 " Enough time passed already, so stop immediately.
187 throw 'Jobs did not complete on time!'
190 call ale#test#WaitForJobs(l:new_deadline)
194 function! ale#test#FlushJobs() abort
195 " The variable is checked for in a loop, as calling one series of
196 " callbacks can trigger a further series of callbacks.
197 while exists('g:ale_run_synchronously_callbacks')
198 let l:callbacks = g:ale_run_synchronously_callbacks
199 unlet g:ale_run_synchronously_callbacks
201 for l:Callback in l:callbacks