]> git.madduck.net Git - etc/vim.git/blobdiff - .vim/bundle/ale/test/lsp/test_lsp_startup.vader

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

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.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

Merge commit '76265755a1add77121c8f9dabb3e9bb70fe9a972' as '.vim/bundle/ale'
[etc/vim.git] / .vim / bundle / ale / test / lsp / test_lsp_startup.vader
diff --git a/.vim/bundle/ale/test/lsp/test_lsp_startup.vader b/.vim/bundle/ale/test/lsp/test_lsp_startup.vader
new file mode 100644 (file)
index 0000000..ef36028
--- /dev/null
@@ -0,0 +1,496 @@
+Before:
+  Save g:ale_run_synchronously
+
+  let g:ale_run_synchronously = 1
+  unlet! g:ale_run_synchronously_callbacks
+  unlet! g:ale_run_synchronously_emulate_commands
+
+  runtime autoload/ale/lsp.vim
+  runtime autoload/ale/lsp_linter.vim
+  runtime autoload/ale/engine.vim
+  runtime autoload/ale/job.vim
+  runtime autoload/ale/socket.vim
+
+  let g:job_map = {}
+  let g:emulate_job_failure = 0
+  let g:next_job_id = 1
+  let g:lsp_started = 0
+
+  let g:socket_map = {}
+  let g:emulate_socket_failure = 0
+  let g:next_channel_id = 0
+
+  let g:message_buffer = ''
+  let g:calls = []
+
+  function! ale#engine#IsExecutable(buffer, executable) abort
+    return !empty(a:executable)
+  endfunction
+
+  function! ale#job#HasOpenChannel(job_id) abort
+    return has_key(g:job_map, a:job_id)
+  endfunction
+
+  function! ale#job#Stop(job_id) abort
+    if has_key(g:job_map, a:job_id)
+      call remove(g:job_map, a:job_id)
+    endif
+  endfunction
+
+  function! ale#job#Start(command, options) abort
+    if g:emulate_job_failure
+      return 0
+    endif
+
+    let l:job_id = g:next_job_id
+    let g:next_job_id += 1
+    let g:job_map[l:job_id] = [a:command, a:options]
+
+    return l:job_id
+  endfunction
+
+  function! ale#job#SendRaw(job_id, data) abort
+    let g:message_buffer .= a:data
+  endfunction
+
+  function! ale#socket#IsOpen(channel_id) abort
+    return has_key(g:socket_map, a:channel_id)
+  endfunction
+
+  function! ale#socket#Close(channel_id) abort
+    if has_key(g:socket_map, a:channel_id)
+      call remove(g:socket_map, a:channel_id)
+    endif
+  endfunction
+
+  function! ale#socket#Open(address, options) abort
+    if g:emulate_socket_failure
+      return -1
+    endif
+
+    let l:channel_id = g:next_channel_id
+    let g:next_channel_id += 1
+    let g:socket_map[l:channel_id] = [a:address, a:options]
+
+    return l:channel_id
+  endfunction
+
+  function! ale#socket#Send(channel_id, data) abort
+    let g:message_buffer .= a:data
+  endfunction
+
+  function! PopMessages() abort
+    let l:message_list = []
+
+    for l:line in split(g:message_buffer, '\(\r\|\n\|Content-Length\)\+')
+      if l:line[:0] is '{'
+        let l:data = json_decode(l:line)
+
+        call add(l:message_list, l:data)
+      endif
+    endfor
+
+    let g:message_buffer = ''
+
+    return l:message_list
+  endfunction
+
+  function! SendMessage(message) abort
+    let l:conn_id = keys(ale#lsp#GetConnections())[0]
+    let l:body = json_encode(a:message)
+    let l:data = 'Content-Length: ' . strlen(l:body) . "\r\n\r\n" . l:body
+
+    call ale#lsp#HandleMessage(l:conn_id, l:data)
+  endfunction
+
+  function! Start(buffer) abort
+    let l:linter = values(ale#linter#GetLintersLoaded())[0][0]
+
+    return ale#lsp_linter#StartLSP(
+    \ a:buffer,
+    \ l:linter,
+    \ {linter, details -> add(g:calls, [linter.name, details])},
+    \)
+  endfunction
+
+  function! AssertInitSuccess(linter_name, conn_prefix, language, root, command, buffer) abort
+    let l:messages = PopMessages()
+
+    if a:linter_name is# 'tsserver'
+      AssertEqual
+      \ [
+      \   {
+      \     'seq': v:null,
+      \     'arguments': {
+      \       'file': expand('#' . a:buffer . ':p'),
+      \     },
+      \     'type': 'request',
+      \     'command': 'open',
+      \   },
+      \ ],
+      \ l:messages
+    else
+      AssertEqual
+      \ [
+      \   {
+      \     'method': 'initialize',
+      \     'jsonrpc': '2.0',
+      \     'id': 1,
+      \     'params': {
+      \       'initializationOptions': {},
+      \       'rootUri': ale#path#ToFileURI(a:root),
+      \       'rootPath': a:root,
+      \       'processId': getpid(),
+      \       'capabilities': {
+      \         'workspace': {
+      \           'applyEdit': v:false,
+      \           'didChangeConfiguration': {
+      \             'dynamicRegistration': v:false,
+      \           },
+      \           'symbol': {
+      \             'dynamicRegistration': v:false,
+      \           },
+      \           'workspaceFolders': v:false,
+      \           'configuration': v:false,
+      \         },
+      \         'textDocument': {
+      \           'synchronization': {
+      \             'dynamicRegistration': v:false,
+      \             'willSave': v:false,
+      \             'willSaveWaitUntil': v:false,
+      \             'didSave': v:true,
+      \           },
+      \           'completion': {
+      \             'dynamicRegistration': v:false,
+      \             'completionItem': {
+      \               'snippetSupport': v:false,
+      \               'commitCharactersSupport': v:false,
+      \               'documentationFormat': ['plaintext', 'markdown'],
+      \               'deprecatedSupport': v:false,
+      \               'preselectSupport': v:false,
+      \             },
+      \             'contextSupport': v:false,
+      \           },
+      \           'hover': {
+      \             'dynamicRegistration': v:false,
+      \             'contentFormat': ['plaintext', 'markdown'],
+      \           },
+      \           'references': {
+      \             'dynamicRegistration': v:false,
+      \           },
+      \           'documentSymbol': {
+      \             'dynamicRegistration': v:false,
+      \             'hierarchicalDocumentSymbolSupport': v:false,
+      \           },
+      \           'definition': {
+      \             'dynamicRegistration': v:false,
+      \             'linkSupport': v:false,
+      \           },
+      \           'typeDefinition': {
+      \             'dynamicRegistration': v:false,
+      \           },
+      \           'implementation': {
+      \             'dynamicRegistration': v:false,
+      \             'linkSupport': v:false,
+      \           },
+      \           'diagnostic': {
+      \             'dynamicRegistration': v:true,
+      \             'relatedDocumentSupport': v:true,
+      \           },
+      \           'publishDiagnostics': {
+      \             'relatedInformation': v:true,
+      \           },
+      \           'codeAction': {
+      \             'dynamicRegistration': v:false,
+      \             'codeActionLiteralSupport': {
+      \                  'codeActionKind': {
+      \                      'valueSet': []
+      \                  }
+      \              }
+      \           },
+      \           'rename': {
+      \             'dynamicRegistration': v:false,
+      \           },
+      \         },
+      \       },
+      \     },
+      \   },
+      \ ],
+      \ l:messages
+
+      call SendMessage({
+      \ 'jsonrpc': '2.0',
+      \ 'id': 1,
+      \ 'result': {
+      \   'capabilities': {
+      \     'renameProvider': v:true,
+      \     'executeCommandProvider': {
+      \       'commands': [],
+      \     },
+      \     'hoverProvider': v:true,
+      \     'documentSymbolProvider': v:true,
+      \     'documentRangeFormattingProvider': v:true,
+      \     'codeLensProvider': {
+      \       'resolveProvider': v:false
+      \     },
+      \     'referencesProvider': v:true,
+      \     'textDocumentSync': 2,
+      \     'documentFormattingProvider': v:true,
+      \     'codeActionProvider': v:true,
+      \     'signatureHelpProvider': {
+      \       'triggerCharacters': ['(', ','],
+      \     },
+      \     'completionProvider': {
+      \       'triggerCharacters': ['.'],
+      \       'resolveProvider': v:false
+      \     },
+      \     'definitionProvider': v:true,
+      \     'experimental': {},
+      \     'documentHighlightProvider': v:true,
+      \     'workspaceSymbolProvider': v:true,
+      \   },
+      \ },
+      \})
+
+      let l:messages = PopMessages()
+
+      AssertEqual
+      \ [
+      \   {
+      \     'method': 'initialized',
+      \     'jsonrpc': '2.0',
+      \     'params': {},
+      \   },
+      \   {
+      \     'method': 'textDocument/didOpen',
+      \     'jsonrpc': '2.0',
+      \     'params': {
+      \       'textDocument': {
+      \         'uri': ale#path#ToFileURI(expand('#' . a:buffer . ':p')),
+      \         'version': ale#lsp#message#GetNextVersionID() - 1,
+      \         'languageId': a:language,
+      \         'text': "\n",
+      \       },
+      \     },
+      \   },
+      \ ],
+      \ l:messages
+    endif
+
+    AssertEqual
+    \ [
+    \   [
+    \     a:linter_name,
+    \     {
+    \       'connection_id': a:conn_prefix . ':' . a:root,
+    \       'project_root': a:root,
+    \       'buffer': a:buffer,
+    \       'command': !empty(a:command) ? ale#job#PrepareCommand(a:buffer, a:command) : '',
+    \     },
+    \   ],
+    \ ],
+    \ g:calls
+  endfunction
+
+  function! AssertInitFailure() abort
+    let l:messages = PopMessages()
+
+    AssertEqual [], l:messages
+    AssertEqual [], g:calls
+  endfunction
+
+  call ale#linter#Reset()
+
+After:
+  Restore
+
+  call ale#linter#Reset()
+  call ale#lsp#ResetConnections()
+
+  unlet! g:ale_run_synchronously_callbacks
+  unlet! g:job_map
+  unlet! g:emulate_job_failure
+  unlet! g:next_job_id
+  unlet! g:lsp_started
+
+  unlet! g:socket_map
+  unlet! g:emulate_socket_failure
+  unlet! g:next_channel_id
+
+  unlet! g:message_buffer
+  unlet! g:calls
+
+  augroup VaderTest
+    autocmd!
+  augroup END
+
+  augroup! VaderTest
+
+  delfunction PopMessages
+  delfunction Start
+  delfunction AssertInitSuccess
+  delfunction AssertInitFailure
+
+  runtime autoload/ale/engine.vim
+  runtime autoload/ale/job.vim
+  runtime autoload/ale/socket.vim
+
+Execute(tsserver should be started correctly):
+  runtime ale_linters/typescript/tsserver.vim
+
+  Assert Start(bufnr(''))
+  call AssertInitSuccess('tsserver', 'tsserver', '', '', ale#Escape('tsserver'), bufnr(''))
+
+Execute(tsserver failures should be handled appropriately):
+  runtime ale_linters/typescript/tsserver.vim
+
+  let g:emulate_job_failure = 1
+
+  Assert !Start(bufnr(''))
+  call AssertInitFailure()
+
+Execute(LSP jobs should start correctly):
+  call ale#linter#Define('foobar', {
+  \   'name': 'foo',
+  \   'lsp': 'stdio',
+  \   'executable': 'foo',
+  \   'command': 'foo',
+  \   'project_root': '/foo/bar',
+  \   'initialization_options': {},
+  \})
+
+  Assert Start(bufnr(''))
+  call AssertInitSuccess('foo', 'foo', 'foobar', '/foo/bar', 'foo', bufnr(''))
+
+Execute(LSP job failures should be handled):
+  call ale#linter#Define('foobar', {
+  \   'name': 'foo',
+  \   'lsp': 'stdio',
+  \   'executable': 'foo',
+  \   'command': 'foo',
+  \   'project_root': '/foo/bar',
+  \   'initialization_options': {},
+  \})
+
+  let g:emulate_job_failure = 1
+
+  Assert !Start(bufnr(''))
+  call AssertInitFailure()
+
+Execute(LSP TCP connections should start correctly):
+  call ale#linter#Define('foobar', {
+  \   'name': 'foo',
+  \   'lsp': 'socket',
+  \   'address': 'foo',
+  \   'project_root': '/foo/bar',
+  \   'initialization_options': {},
+  \})
+
+  Assert Start(bufnr(''))
+  call AssertInitSuccess('foo', 'foo', 'foobar', '/foo/bar', '', bufnr(''))
+
+Execute(LSP TCP connection failures should be handled):
+  call ale#linter#Define('foobar', {
+  \   'name': 'foo',
+  \   'lsp': 'socket',
+  \   'address': 'foo',
+  \   'project_root': '/foo/bar',
+  \   'initialization_options': {},
+  \})
+
+  let g:emulate_socket_failure = 1
+
+  Assert !Start(bufnr(''))
+  call AssertInitFailure()
+
+Execute(Deferred executables should be handled correctly):
+  call ale#linter#Define('foobar', {
+  \   'name': 'foo',
+  \   'lsp': 'stdio',
+  \   'executable': {b -> ale#command#Run(b, 'echo', {-> 'foo'})},
+  \   'command': '%e -c',
+  \   'project_root': '/foo/bar',
+  \   'initialization_options': {},
+  \})
+
+  Assert Start(bufnr(''))
+  call ale#test#FlushJobs()
+  call AssertInitSuccess('foo', 'foo', 'foobar', '/foo/bar', ale#Escape('foo') . ' -c', bufnr(''))
+
+Execute(Deferred commands should be handled correctly):
+  call ale#linter#Define('foobar', {
+  \   'name': 'foo',
+  \   'lsp': 'stdio',
+  \   'executable': 'foo',
+  \   'command': {b -> ale#command#Run(b, 'echo', {-> '%e -c'})},
+  \   'project_root': '/foo/bar',
+  \   'initialization_options': {},
+  \})
+
+  Assert Start(bufnr(''))
+  call ale#test#FlushJobs()
+  call AssertInitSuccess('foo', 'foo', 'foobar', '/foo/bar', ale#Escape('foo') . ' -c', bufnr(''))
+
+Execute(Deferred addresses should be handled correctly):
+  call ale#linter#Define('foobar', {
+  \   'name': 'foo',
+  \   'lsp': 'socket',
+  \   'address': {b -> ale#command#Run(b, 'echo', {-> 'localhost:1234'})},
+  \   'project_root': '/foo/bar',
+  \   'initialization_options': {},
+  \})
+
+  Assert Start(bufnr(''))
+  call ale#test#FlushJobs()
+  call AssertInitSuccess('foo', 'localhost:1234', 'foobar', '/foo/bar', '', bufnr(''))
+
+Execute(Servers that have crashed should be restarted):
+  call ale#lsp#Register('foo', '/foo/bar', '', {})
+  call extend(ale#lsp#GetConnections()['foo:/foo/bar'], {'initialized': 1})
+
+  " Starting the program again should reset initialized to `0`.
+  call ale#lsp#StartProgram('foo:/foo/bar', 'foobar', 'foobar --start')
+
+  AssertEqual 0, ale#lsp#GetConnections()['foo:/foo/bar']['initialized']
+  AssertEqual ['initialize'], map(PopMessages(), 'v:val[''method'']')
+
+Execute(Current LSP buffer should receive ALELSPStarted):
+  call ale#linter#Define('foobar', {
+  \   'name': 'foo',
+  \   'lsp': 'socket',
+  \   'address': 'foo',
+  \   'project_root': '/foo/bar',
+  \   'initialization_options': {},
+  \})
+
+  augroup VaderTest
+    autocmd!
+    autocmd User ALELSPStarted let g:lsp_started = 1
+  augroup END
+
+  Assert Start(bufnr(''))
+  call AssertInitSuccess('foo', 'foo', 'foobar', '/foo/bar', '', bufnr(''))
+  AssertEqual g:lsp_started, 1
+
+Execute(Target LSP buffer should receive ALELSPStarted):
+  call ale#linter#Define('foobar', {
+  \   'name': 'foo',
+  \   'lsp': 'socket',
+  \   'address': 'foo',
+  \   'project_root': '/foo/bar',
+  \   'initialization_options': {},
+  \})
+
+  augroup VaderTest
+    autocmd!
+    autocmd User ALELSPStarted let g:lsp_started = 1
+  augroup END
+
+  let buffer = bufnr('')
+
+  enew!
+  Assert Start(buffer)
+  call AssertInitSuccess('foo', 'foo', 'foobar', '/foo/bar', '', buffer)
+  execute 'buffer' . buffer
+
+  AssertEqual g:lsp_started, 1