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
