Before: Save g:ale_enabled Save g:ale_run_synchronously Save g:ale_set_lists_synchronously Save g:ale_buffer_info let g:ale_enabled = 1 let g:ale_buffer_info = {} let g:ale_run_synchronously = 1 let g:ale_set_lists_synchronously = 1 function! TestCallback(buffer, output) " Windows adds extra spaces to the text from echo. return [{ \ 'lnum': 2, \ 'col': 3, \ 'text': 'testlinter1', \}] endfunction function! TestCallback2(buffer, output) " Windows adds extra spaces to the text from echo. return [{ \ 'lnum': 1, \ 'col': 3, \ 'text': 'testlinter2', \}] endfunction function! TestCallback3(buffer, output) " Windows adds extra spaces to the text from echo. return [{ \ 'lnum': 3, \ 'col': 3, \ 'text': 'testlinter3', \}] endfunction " These two linters computer their lint_file values after running commands. call ale#linter#Define('foobar', { \ 'name': 'testlinter1', \ 'callback': 'TestCallback', \ 'executable': has('win32') ? 'cmd' : 'echo', \ 'command': has('win32') ? 'echo foo bar' : '/bin/sh -c ''echo foo bar''', \ 'lint_file': {b -> ale#command#Run(b, 'echo', {-> 1})}, \}) call ale#linter#Define('foobar', { \ 'name': 'testlinter2', \ 'callback': 'TestCallback2', \ 'executable': has('win32') ? 'cmd' : 'echo', \ 'command': has('win32') ? 'echo foo bar' : '/bin/sh -c ''echo foo bar''', \ 'lint_file': {b -> ale#command#Run(b, 'echo', {-> ale#command#Run(b, 'echo', {-> 1})})}, \}) " This one directly computes the result. call ale#linter#Define('foobar', { \ 'name': 'testlinter3', \ 'callback': 'TestCallback3', \ 'executable': has('win32') ? 'cmd' : 'echo', \ 'command': has('win32') ? 'echo foo bar' : '/bin/sh -c ''echo foo bar''', \ 'lint_file': {b -> 1}, \}) let g:filename = tempname() call writefile([], g:filename) call ale#test#SetFilename(g:filename) After: delfunction TestCallback call ale#engine#Cleanup(bufnr('')) Restore call ale#linter#Reset() " Items and markers, etc. call setloclist(0, []) call clearmatches() call ale#sign#Clear() if filereadable(g:filename) call delete(g:filename) endif unlet g:filename Given foobar(A file with some lines): foo bar baz Execute(lint_file results where the result is eventually computed should be run): call ale#Queue(0, 'lint_file') call ale#test#FlushJobs() AssertEqual \ [ \ { \ 'bufnr': bufnr('%'), \ 'lnum': 1, \ 'vcol': 0, \ 'col': 3, \ 'text': 'testlinter2', \ 'type': 'E', \ 'nr': -1, \ 'pattern': '', \ 'valid': 1, \ }, \ { \ 'bufnr': bufnr('%'), \ 'lnum': 2, \ 'vcol': 0, \ 'col': 3, \ 'text': 'testlinter1', \ 'type': 'E', \ 'nr': -1, \ 'pattern': '', \ 'valid': 1, \ }, \ { \ 'bufnr': bufnr('%'), \ 'lnum': 3, \ 'vcol': 0, \ 'col': 3, \ 'text': 'testlinter3', \ 'type': 'E', \ 'nr': -1, \ 'pattern': '', \ 'valid': 1, \ }, \ ], \ ale#test#GetLoclistWithoutNewerKeys() Execute(Linters where lint_file eventually evaluates to 1 shouldn't be run if we don't want to run them): call ale#Queue(0, '') call ale#test#FlushJobs() AssertEqual [], ale#test#GetLoclistWithoutNewerKeys() Execute(Keeping computed lint_file jobs running should work): AssertEqual 'testlinter2', ale#linter#Get('foobar')[1].name call ale#engine#InitBufferInfo(bufnr('')) call ale#engine#MarkLinterActive( \ g:ale_buffer_info[bufnr('')], \ ale#linter#Get('foobar')[1] \) call ale#engine#RunLinters(bufnr(''), ale#linter#Get('foobar'), 0) Assert !empty(g:ale_buffer_info[bufnr('')].active_linter_list), \ 'The active linter list was empty' Assert ale#engine#IsCheckingBuffer(bufnr('')), \ 'The IsCheckingBuffer function returned 0'