Before: Save g:ale_fix_on_save Save g:ale_enabled Save g:ale_run_synchronously Save g:ale_set_lists_synchronously Save g:ale_buffer_info Save g:ale_linters let g:ale_buffer_info = {} let g:ale_run_synchronously = 1 unlet! g:ale_run_synchronously_callbacks let g:ale_set_lists_synchronously = 1 let b:ale_save_event_fired = 0 let g:buffer_result = [ \ { \ 'lnum': 1, \ 'col': 1, \ 'text': 'buffer error', \ 'type': 'E', \ }, \ { \ 'lnum': 2, \ 'col': 1, \ 'text': 'buffer warning', \ 'type': 'W', \ }, \] function! LintFileCallback(buffer, output) return [ \ { \ 'lnum': 1, \ 'col': 3, \ 'text': 'file warning', \ 'type': 'W', \ }, \ { \ 'lnum': 2, \ 'col': 3, \ 'text': 'file error', \ 'type': 'E', \ }, \] endfunction function! BufferCallback(buffer, output) return deepcopy(g:buffer_result) endfunction function! GetSimplerLoclist() let l:loclist = [] for l:item in ale#test#GetLoclistWithoutModule() call add(l:loclist, { \ 'lnum': l:item.lnum, \ 'col': l:item.col, \ 'text': l:item.text, \ 'type': l:item.type, \}) endfor return l:loclist endfunction call ale#linter#Define('foobar', { \ 'name': 'lint_file_linter', \ 'callback': 'LintFileCallback', \ 'executable': has('win32') ? 'cmd' : 'echo', \ 'command': 'echo', \ 'lint_file': 1, \}) call ale#linter#Define('foobar', { \ 'name': 'buffer_linter', \ 'callback': 'BufferCallback', \ 'executable': has('win32') ? 'cmd' : 'echo', \ 'command': 'echo', \ 'read_buffer': 0, \}) let g:filename = tempname() call writefile([], g:filename) call ale#test#SetFilename(g:filename) After: if !g:ale_run_synchronously call ale#engine#Cleanup(bufnr('')) endif Restore unlet! g:ale_run_synchronously_callbacks unlet! b:ale_save_event_fired unlet! b:ale_enabled unlet g:buffer_result let g:ale_buffer_info = {} call ale#linter#Reset() call setloclist(0, []) delfunction LintFileCallback delfunction BufferCallback if filereadable(g:filename) call delete(g:filename) endif unlet g:filename Given foobar (Some imaginary filetype): foo bar baz Execute(Running linters without 'lint_file' should run only buffer linters): call ale#Queue(0) call ale#test#FlushJobs() AssertEqual [ \ { \ 'lnum': 1, \ 'col': 1, \ 'text': 'buffer error', \ 'type': 'E', \ }, \ { \ 'lnum': 2, \ 'col': 1, \ 'text': 'buffer warning', \ 'type': 'W', \ }, \], GetSimplerLoclist() Execute(Running linters with 'lint_file' should run all linters): Assert filereadable(expand('%:p')), 'The file was not readable' call ale#Queue(0, 'lint_file') call ale#test#FlushJobs() AssertEqual [ \ { \ 'lnum': 1, \ 'col': 1, \ 'text': 'buffer error', \ 'type': 'E', \ }, \ { \ 'lnum': 1, \ 'col': 3, \ 'text': 'file warning', \ 'type': 'W', \ }, \ { \ 'lnum': 2, \ 'col': 1, \ 'text': 'buffer warning', \ 'type': 'W', \ }, \ { \ 'lnum': 2, \ 'col': 3, \ 'text': 'file error', \ 'type': 'E', \ }, \], GetSimplerLoclist() Execute(Linter errors from files should be kept): Assert filereadable(expand('%:p')), 'The file was not readable' call ale#Queue(0, 'lint_file') call ale#test#FlushJobs() " Change the results for the buffer callback. let g:buffer_result = [ \ { \ 'lnum': 1, \ 'col': 1, \ 'text': 'new buffer error', \ 'type': 'E', \ }, \] call ale#Queue(0) call ale#test#FlushJobs() AssertEqual [ \ { \ 'lnum': 1, \ 'col': 1, \ 'text': 'new buffer error', \ 'type': 'E', \ }, \ { \ 'lnum': 1, \ 'col': 3, \ 'text': 'file warning', \ 'type': 'W', \ }, \ { \ 'lnum': 2, \ 'col': 3, \ 'text': 'file error', \ 'type': 'E', \ }, \], GetSimplerLoclist() Execute(Linter errors from files should be kept when no other linters are run): let g:ale_linters = {'foobar': ['lint_file_linter']} Assert filereadable(expand('%:p')), 'The file was not readable' call ale#Queue(0, 'lint_file') call ale#test#FlushJobs() AssertEqual [ \ { \ 'lnum': 1, \ 'col': 3, \ 'text': 'file warning', \ 'type': 'W', \ }, \ { \ 'lnum': 2, \ 'col': 3, \ 'text': 'file error', \ 'type': 'E', \ }, \], GetSimplerLoclist() call ale#Queue(0) AssertEqual [ \ { \ 'lnum': 1, \ 'col': 3, \ 'text': 'file warning', \ 'type': 'W', \ }, \ { \ 'lnum': 2, \ 'col': 3, \ 'text': 'file error', \ 'type': 'E', \ }, \], GetSimplerLoclist() Execute(The Save event should respect the buffer number): let g:ale_linters = {'foobar': ['lint_file_linter']} Assert filereadable(expand('%:p')), 'The file was not readable' call ale#events#SaveEvent(bufnr('') + 1) call ale#test#FlushJobs() " We shouldn't get any prblems yet. AssertEqual [], GetSimplerLoclist() call ale#events#SaveEvent(bufnr('')) call ale#test#FlushJobs() " We should get them now we used the right buffer number. AssertEqual [ \ { \ 'lnum': 1, \ 'col': 3, \ 'text': 'file warning', \ 'type': 'W', \ }, \ { \ 'lnum': 2, \ 'col': 3, \ 'text': 'file error', \ 'type': 'E', \ }, \], GetSimplerLoclist() Execute(The Save event should set b:ale_save_event_fired to 1): let g:ale_lint_on_save = 1 let b:ale_enabled = 1 call ale#linter#Reset() call ale#events#SaveEvent(bufnr('')) call ale#test#FlushJobs() " This flag needs to be set so windows can be opened, etc. AssertEqual 1, b:ale_save_event_fired Execute(b:ale_save_event_fired should be set to 0 when results are set): let b:ale_save_event_fired = 1 call ale#engine#SetResults(bufnr(''), []) call ale#test#FlushJobs() AssertEqual 0, b:ale_save_event_fired Execute(lint_file linters should stay running after checking without them): let g:ale_run_synchronously = 0 " Run all linters, then just the buffer linters. call ale#Queue(0, 'lint_file') call ale#Queue(0) " The lint_file linter should still be running. AssertEqual \ ['lint_file_linter', 'buffer_linter'], \ map(copy(g:ale_buffer_info[bufnr('')].active_linter_list), 'v:val.name') " We should have 1 job for each linter. AssertEqual \ 2, \ len(keys(get(get(ale#command#GetData(), bufnr(''), {}), 'jobs', {}))) call ale#test#WaitForJobs(2000) Execute(The save event should not lint the buffer when ALE is disabled): let g:ale_enabled = 0 call ale#events#SaveEvent(bufnr('')) call ale#test#FlushJobs() AssertEqual [], GetSimplerLoclist() AssertEqual 0, b:ale_save_event_fired