Before:
  Save g:ale_lsp_root
  Save b:ale_lsp_root

  let g:ale_lsp_root = {}
  unlet! b:ale_lsp_root

  let g:linter = {}

After:
  unlet g:linter

Execute (PreProcess should throw when the linter object is not a Dictionary):
  AssertThrows call ale#linter#PreProcess('testft', '')
  AssertEqual 'The linter object must be a Dictionary', g:vader_exception

Execute (PreProcess should throw when there is no name):
  AssertThrows call ale#linter#PreProcess('testft', {
  \ 'callback': 'SomeFunction',
  \ 'executable': 'echo',
  \ 'command': 'echo',
  \})
  AssertEqual '`name` must be defined to name the linter', g:vader_exception

Execute (PreProcess should throw when there is no callback):
  AssertThrows call ale#linter#PreProcess('testft', {
  \ 'name': 'foo',
  \ 'executable': 'echo',
  \ 'command': 'echo',
  \})
  AssertEqual '`callback` must be defined with a callback to accept output', g:vader_exception

Execute (PreProcess should throw when then callback is not a function):
  AssertThrows call ale#linter#PreProcess('testft', {
  \ 'name': 'foo',
  \ 'callback': 1,
  \ 'executable': 'echo',
  \ 'command': 'echo',
  \})
  AssertEqual '`callback` must be defined with a callback to accept output', g:vader_exception

Execute (PreProcess should throw when there is no executable or executable_callback):
  AssertThrows call ale#linter#PreProcess('testft', {
  \ 'name': 'foo',
  \ 'callback': 'SomeFunction',
  \ 'command': 'echo',
  \})
  AssertEqual 'Either `executable` or `executable_callback` must be defined', g:vader_exception

Execute (PreProcess should throw when executable is not a string):
  AssertThrows call ale#linter#PreProcess('testft', {
  \ 'name': 'foo',
  \ 'callback': 'SomeFunction',
  \ 'executable': 123,
  \ 'command': 'echo',
  \})
  AssertEqual '`executable` must be a String or Function if defined', g:vader_exception

Execute (PreProcess should throw when executable_callback is not a callback):
  AssertThrows call ale#linter#PreProcess('testft', {
  \ 'name': 'foo',
  \ 'callback': 'SomeFunction',
  \ 'executable_callback': 123,
  \ 'command': 'echo',
  \})
  AssertEqual '`executable_callback` must be a callback if defined', g:vader_exception

Execute (PreProcess should allow executable to be a callback):
  call ale#linter#PreProcess('testft', {
  \ 'name': 'foo',
  \ 'callback': 'SomeFunction',
  \ 'executable': function('type'),
  \ 'command': 'echo',
  \})

Execute (PreProcess should throw when there is no command):
  AssertThrows call ale#linter#PreProcess('testft', {
  \ 'name': 'foo',
  \ 'callback': 'SomeFunction',
  \ 'executable': 'echo',
  \})
  AssertEqual 'Either `command`, `executable_callback`, `command_chain` must be defined', g:vader_exception

Execute (PreProcess should throw when command is not a string):
  AssertThrows call ale#linter#PreProcess('testft', {
  \ 'name': 'foo',
  \ 'callback': 'SomeFunction',
  \ 'executable': 'echo',
  \ 'command': [],
  \})
  AssertEqual '`command` must be a String or Function if defined', g:vader_exception

Execute (PreProcess should allow command to be a callback):
  call ale#linter#PreProcess('testft', {
  \ 'name': 'foo',
  \ 'callback': 'SomeFunction',
  \ 'executable': 'echo',
  \ 'command': function('type'),
  \})

Execute (PreProcess should throw when command_callback is not a callback):
  AssertThrows call ale#linter#PreProcess('testft', {
  \ 'name': 'foo',
  \ 'callback': 'SomeFunction',
  \ 'executable': 'echo',
  \ 'command_callback': 123,
  \})
  AssertEqual '`command_callback` must be a callback if defined', g:vader_exception

Execute (PreProcess should when the output stream isn't a valid string):
  AssertThrows call ale#linter#PreProcess('testft', {
  \ 'name': 'foo',
  \ 'callback': 'SomeFunction',
  \ 'executable': 'echo',
  \ 'command': 'echo',
  \ 'output_stream': 'xxx',
  \})
  AssertEqual "`output_stream` must be 'stdout', 'stderr', or 'both'", g:vader_exception

Execute (PreProcess should not throw when everything is correct):
  call ale#linter#PreProcess('testft', {
  \ 'name': 'foo',
  \ 'callback': 'SomeFunction',
  \ 'executable': 'echo',
  \ 'command': 'echo',
  \})

Execute (PreProcess should accept an stdout output_stream):
  call ale#linter#PreProcess('testft', {
  \ 'name': 'foo',
  \ 'callback': 'SomeFunction',
  \ 'executable': 'echo',
  \ 'command': 'echo',
  \ 'output_stream': 'stdout',
  \})

Execute (PreProcess should accept an stderr output_stream):
  call ale#linter#PreProcess('testft', {
  \ 'name': 'foo',
  \ 'callback': 'SomeFunction',
  \ 'executable': 'echo',
  \ 'command': 'echo',
  \ 'output_stream': 'stderr',
  \})

Execute (PreProcess should accept a 'both' output_stream):
  call ale#linter#PreProcess('testft', {
  \ 'name': 'foo',
  \ 'callback': 'SomeFunction',
  \ 'executable': 'echo',
  \ 'command': 'echo',
  \ 'output_stream': 'both',
  \})

Execute(PreProcess should complain if the command_chain is not a List):
  let g:linter = {
  \ 'name': 'x',
  \ 'callback': 'x',
  \ 'executable': 'x',
  \ 'command_chain': 'x',
  \}
  AssertThrows call ale#linter#PreProcess('testft', g:linter)
  AssertEqual '`command_chain` must be a List', g:vader_exception

Execute(PreProcess should complain if the command_chain is empty):
  let g:linter = {
  \ 'name': 'x',
  \ 'callback': 'x',
  \ 'executable': 'x',
  \ 'command_chain': [],
  \}
  AssertThrows call ale#linter#PreProcess('testft', g:linter)
  AssertEqual '`command_chain` must contain at least one item', g:vader_exception

Execute(PreProcess should complain if the command_chain has no callback):
  let g:linter = {
  \ 'name': 'x',
  \ 'callback': 'x',
  \ 'executable': 'x',
  \ 'command_chain': [{}],
  \}
  AssertThrows call ale#linter#PreProcess('testft', g:linter)
  AssertEqual 'The `command_chain` item 0 must define a `callback` function', g:vader_exception

Execute(PreProcess should complain if the command_chain callback is not a function):
  let g:linter = {
  \ 'name': 'x',
  \ 'callback': 'x',
  \ 'executable': 'x',
  \ 'command_chain': [{'callback': 2}],
  \}
  AssertThrows call ale#linter#PreProcess('testft', g:linter)
  AssertEqual 'The `command_chain` item 0 must define a `callback` function', g:vader_exception

Execute(PreProcess should accept a chain with one callback):
  let g:linter = {
  \ 'name': 'x',
  \ 'callback': 'x',
  \ 'executable': 'x',
  \ 'command_chain': [{'callback': 'foo'}],
  \}
  call ale#linter#PreProcess('testft', g:linter)

Execute(PreProcess should complain about invalid output_stream values in the chain):
  let g:linter = {
  \ 'name': 'x',
  \ 'callback': 'x',
  \ 'executable': 'x',
  \ 'command_chain': [{'callback': 'foo', 'output_stream': ''}],
  \}
  AssertThrows call ale#linter#PreProcess('testft', g:linter)
  AssertEqual "The `command_chain` item 0 `output_stream` flag must be 'stdout', 'stderr', or 'both'", g:vader_exception

Execute(PreProcess should complain about valid output_stream values in the chain):
  let g:linter = {
  \ 'name': 'x',
  \ 'callback': 'x',
  \ 'executable': 'x',
  \ 'command_chain': [{'callback': 'foo', 'output_stream': 'stdout'}],
  \}
  call ale#linter#PreProcess('testft', g:linter)
  let g:linter.command_chain[0].output_stream = 'stderr'
  call ale#linter#PreProcess('testft', g:linter)
  let g:linter.command_chain[0].output_stream = 'both'
  call ale#linter#PreProcess('testft', g:linter)

Execute(PreProcess should complain about invalid chain items at higher indices):
  let g:linter = {
  \ 'name': 'x',
  \ 'callback': 'x',
  \ 'executable': 'x',
  \ 'command_chain': [{'callback': 'foo'}, {'callback': 123}],
  \}
  AssertThrows call ale#linter#PreProcess('testft', g:linter)
  AssertEqual 'The `command_chain` item 1 must define a `callback` function', g:vader_exception

Execute(PreProcess should complain when conflicting command options are used):
  let g:linter = {
  \ 'name': 'x',
  \ 'callback': 'x',
  \ 'executable': 'x',
  \ 'command': 'foo',
  \ 'command_chain': [{'callback': 'foo'}],
  \}
  AssertThrows call ale#linter#PreProcess('testft', g:linter)
  AssertEqual 'Only one of `command`, `command_callback`, or `command_chain` should be set', g:vader_exception

  unlet g:linter.command
  let g:linter.command_callback = 'foo'

  AssertThrows call ale#linter#PreProcess('testft', g:linter)
  AssertEqual 'Only one of `command`, `command_callback`, or `command_chain` should be set', g:vader_exception

  let g:linter.command = 'foo'
  unlet g:linter.command_chain

  AssertThrows call ale#linter#PreProcess('testft', g:linter)
  AssertEqual 'Only one of `command`, `command_callback`, or `command_chain` should be set', g:vader_exception

Execute(PreProcess should process the read_buffer option correctly):
  let g:linter = {
  \ 'name': 'x',
  \ 'callback': 'x',
  \ 'executable': 'x',
  \ 'command_chain': [{'callback': 'foo'}, {'callback': 'bar'}],
  \ 'read_buffer': '0',
  \}

  AssertThrows call ale#linter#PreProcess('testft', g:linter)
  AssertEqual '`read_buffer` must be `0` or `1`', g:vader_exception

  let g:linter.read_buffer = 0

  call ale#linter#PreProcess('testft', g:linter)

  let g:linter.read_buffer = 1

  call ale#linter#PreProcess('testft', g:linter)

  unlet g:linter.read_buffer
  let g:linter.command_chain[0].read_buffer = '0'

  AssertThrows call ale#linter#PreProcess('testft', g:linter)
  AssertEqual 'The `command_chain` item 0 value for `read_buffer` must be `0` or `1`', g:vader_exception

  let g:linter.command_chain[0].read_buffer = 0

  call ale#linter#PreProcess('testft', g:linter)

  let g:linter.command_chain[1].read_buffer = '0'

  AssertThrows call ale#linter#PreProcess('testft', g:linter)
  AssertEqual 'The `command_chain` item 1 value for `read_buffer` must be `0` or `1`', g:vader_exception

  let g:linter.command_chain[1].read_buffer = 1

  call ale#linter#PreProcess('testft', g:linter)

Execute(PreProcess should set a default value for read_buffer):
  let g:linter = {
  \ 'name': 'x',
  \ 'callback': 'x',
  \ 'executable': 'x',
  \ 'command': 'x',
  \}

  AssertEqual 1, ale#linter#PreProcess('testft', g:linter).read_buffer

Execute(PreProcess should process the lint_file option correctly):
  let g:linter = {
  \ 'name': 'x',
  \ 'callback': 'x',
  \ 'executable': 'x',
  \ 'command': 'x',
  \ 'lint_file': 'x',
  \}

  AssertThrows call ale#linter#PreProcess('testft', g:linter)
  AssertEqual '`lint_file` must be `0` or `1`', g:vader_exception

  let g:linter.lint_file = 0

  AssertEqual 0, ale#linter#PreProcess('testft', g:linter).lint_file
  " The default for read_buffer should be 1 when lint_file is 0
  AssertEqual 1, ale#linter#PreProcess('testft', g:linter).read_buffer

  let g:linter.lint_file = 1

  AssertEqual 1, ale#linter#PreProcess('testft', g:linter).lint_file
  " The default for read_buffer should change to 0 when lint_file is 1.
  AssertEqual 0, ale#linter#PreProcess('testft', g:linter).read_buffer

  let g:linter.read_buffer = 1

  " We shouldn't be able to set both options to 1 at the same time.
  AssertThrows call ale#linter#PreProcess('testft', g:linter)
  AssertEqual 'Only one of `lint_file` or `read_buffer` can be `1`', g:vader_exception

Execute(PreProcess should set a default value for lint_file):
  let g:linter = {
  \ 'name': 'x',
  \ 'callback': 'x',
  \ 'executable': 'x',
  \ 'command': 'x',
  \}

  AssertEqual 0, ale#linter#PreProcess('testft', g:linter).lint_file

Execute(PreProcess should set a default value for aliases):
  let g:linter = {
  \ 'name': 'x',
  \ 'callback': 'x',
  \ 'executable': 'x',
  \ 'command': 'x',
  \}

  AssertEqual [], ale#linter#PreProcess('testft', g:linter).aliases

Execute(PreProcess should complain about invalid `aliases` values):
  let g:linter = {
  \ 'name': 'x',
  \ 'callback': 'x',
  \ 'executable': 'x',
  \ 'command': 'x',
  \ 'aliases': 'foo',
  \}

  AssertThrows call ale#linter#PreProcess('testft', g:linter)
  AssertEqual '`aliases` must be a List of String values', g:vader_exception

  let g:linter.aliases = [1]

  AssertThrows call ale#linter#PreProcess('testft', g:linter)
  AssertEqual '`aliases` must be a List of String values', g:vader_exception

Execute(PreProcess should accept `aliases` lists):
  let g:linter = {
  \ 'name': 'x',
  \ 'callback': 'x',
  \ 'executable': 'x',
  \ 'command': 'x',
  \ 'aliases': [],
  \}

  AssertEqual [], ale#linter#PreProcess('testft', g:linter).aliases

  let g:linter.aliases = ['foo', 'bar']

  AssertEqual ['foo', 'bar'], ale#linter#PreProcess('testft', g:linter).aliases

Execute(PreProcess should accept tsserver LSP configuration):
  let g:linter = {
  \ 'name': 'x',
  \ 'executable': 'x',
  \ 'command': 'x',
  \ 'lsp': 'tsserver',
  \ 'language_callback': 'x',
  \ 'project_root_callback': 'x',
  \}

  AssertEqual 'tsserver', ale#linter#PreProcess('testft', g:linter).lsp

  call remove(g:linter, 'executable')
  let g:linter.executable_callback = 'X'

  call ale#linter#PreProcess('testft', g:linter)

  call remove(g:linter, 'command')
  let g:linter.command_callback = 'X'

  call ale#linter#PreProcess('testft', g:linter)

Execute(PreProcess should accept stdio LSP configuration):
  let g:linter = {
  \ 'name': 'x',
  \ 'executable': 'x',
  \ 'command': 'x',
  \ 'lsp': 'stdio',
  \ 'language_callback': 'x',
  \ 'project_root_callback': 'x',
  \}

  AssertEqual 'stdio', ale#linter#PreProcess('testft', g:linter).lsp

  call remove(g:linter, 'executable')
  let g:linter.executable_callback = 'X'

  call ale#linter#PreProcess('testft', g:linter)

  call remove(g:linter, 'command')
  let g:linter.command_callback = 'X'

  call ale#linter#PreProcess('testft', g:linter)

Execute(PreProcess should accept LSP server configurations):
  let g:linter = {
  \ 'name': 'x',
  \ 'lsp': 'socket',
  \ 'address_callback': 'X',
  \ 'language_callback': 'x',
  \ 'project_root_callback': 'x',
  \}

  AssertEqual 'socket', ale#linter#PreProcess('testft', g:linter).lsp

Execute(PreProcess should accept let you specify the language as just a string):
  let g:linter = {
  \ 'name': 'x',
  \ 'lsp': 'socket',
  \ 'address_callback': 'X',
  \ 'language': 'foobar',
  \ 'project_root_callback': 'x',
  \}

  AssertEqual 'foobar', ale#linter#PreProcess('testft', g:linter).language_callback(0)

Execute(PreProcess should complain about using language and language_callback together):
  let g:linter = {
  \ 'name': 'x',
  \ 'lsp': 'socket',
  \ 'address_callback': 'X',
  \ 'language': 'x',
  \ 'language_callback': 'x',
  \ 'project_root_callback': 'x',
  \}

  AssertThrows call ale#linter#PreProcess('testft', g:linter)
  AssertEqual 'Only one of `language` or `language_callback` should be set', g:vader_exception

Execute(PreProcess should complain about invalid language values):
  let g:linter = {
  \ 'name': 'x',
  \ 'lsp': 'socket',
  \ 'address_callback': 'X',
  \ 'language': 0,
  \ 'project_root_callback': 'x',
  \}

  AssertThrows call ale#linter#PreProcess('testft', g:linter)
  AssertEqual '`language` must be a String or Funcref', g:vader_exception

Execute(PreProcess should use the filetype as the language string by default):
  let g:linter = {
  \ 'name': 'x',
  \ 'lsp': 'socket',
  \ 'address_callback': 'X',
  \ 'project_root_callback': 'x',
  \}

  AssertEqual 'testft', ale#linter#PreProcess('testft', g:linter).language_callback(0)

Execute(PreProcess should allow language to be set to a callback):
  let g:linter = {
  \ 'name': 'x',
  \ 'lsp': 'socket',
  \ 'address_callback': 'X',
  \ 'language': {-> 'foo'},
  \ 'project_root_callback': 'x',
  \}

  AssertEqual 'foo', ale#linter#PreProcess('testft', g:linter).language_callback(0)

Execute(PreProcess should require an address_callback for LSP socket configurations):
  let g:linter = {
  \ 'name': 'x',
  \ 'lsp': 'socket',
  \}

  AssertThrows call ale#linter#PreProcess('testft', g:linter)
  AssertEqual '`address` or `address_callback` must be defined for getting the LSP address', g:vader_exception

Execute(PreProcess should complain about address_callback for non-LSP linters):
  let g:linter = {
  \ 'name': 'x',
  \ 'callback': 'SomeFunction',
  \ 'executable': 'echo',
  \ 'command': 'echo',
  \ 'address_callback': 'X',
  \}

  AssertThrows call ale#linter#PreProcess('testft', g:linter)
  AssertEqual '`address` or `address_callback` cannot be used when lsp != ''socket''', g:vader_exception

Execute(PreProcess accept valid address_callback values):
  let g:linter = ale#linter#PreProcess('testft', {
  \ 'name': 'x',
  \ 'lsp': 'socket',
  \ 'address_callback': {-> 'foo:123'},
  \ 'language': 'x',
  \ 'project_root_callback': 'x',
  \})

  AssertEqual 'foo:123', ale#linter#GetAddress(0, g:linter)

Execute(PreProcess accept address as a String):
  let g:linter = ale#linter#PreProcess('testft', {
  \ 'name': 'x',
  \ 'lsp': 'socket',
  \ 'address': 'foo:123',
  \ 'language': 'x',
  \ 'project_root_callback': 'x',
  \})

  AssertEqual 'foo:123', ale#linter#GetAddress(0, g:linter)

Execute(PreProcess accept address as a Function):
  let g:linter = ale#linter#PreProcess('testft', {
  \ 'name': 'x',
  \ 'lsp': 'socket',
  \ 'address': {-> 'foo:123'},
  \ 'language': 'x',
  \ 'project_root_callback': 'x',
  \})

  AssertEqual 'foo:123', ale#linter#GetAddress(0, g:linter)

Execute(PreProcess should complain about invalid address values):
  AssertThrows call ale#linter#PreProcess('testft', {
  \ 'name': 'x',
  \ 'lsp': 'socket',
  \ 'address': 0,
  \ 'language': 'x',
  \ 'project_root_callback': 'x',
  \})
  AssertEqual '`address` must be a String or Function if defined', g:vader_exception

Execute(PreProcess should accept allow the project root be set as a String):
  let g:linter = ale#linter#PreProcess('testft', {
  \ 'name': 'x',
  \ 'lsp': 'socket',
  \ 'address': 'foo:123',
  \ 'language': 'x',
  \ 'project_root': '/foo/bar',
  \})

  AssertEqual '/foo/bar', ale#lsp_linter#FindProjectRoot(0, g:linter)

Execute(PreProcess should accept allow the project root be set as a Function):
  let g:linter = ale#linter#PreProcess('testft', {
  \ 'name': 'x',
  \ 'lsp': 'socket',
  \ 'address': 'foo:123',
  \ 'language': 'x',
  \ 'project_root': {-> '/foo/bar'},
  \})

  AssertEqual '/foo/bar', ale#lsp_linter#FindProjectRoot(0, g:linter)

Execute(PreProcess should complain when the project_root valid is invalid):
  AssertThrows call ale#linter#PreProcess('testft', {
  \ 'name': 'x',
  \ 'lsp': 'socket',
  \ 'address': 'foo:123',
  \ 'language': 'x',
  \ 'project_root': 0,
  \})
  AssertEqual '`project_root` must be a String or Function if defined', g:vader_exception

Execute(PreProcess should accept project_root_callback as a String):
  call ale#linter#PreProcess('testft', {
  \ 'name': 'x',
  \ 'lsp': 'socket',
  \ 'address': 'foo:123',
  \ 'language': 'x',
  \ 'project_root_callback': 'Foobar',
  \})

Execute(PreProcess should accept project_root_callback as a Function):
  let g:linter = ale#linter#PreProcess('testft', {
  \ 'name': 'x',
  \ 'lsp': 'socket',
  \ 'address': 'foo:123',
  \ 'language': 'x',
  \ 'project_root_callback': {-> '/foo/bar'},
  \})

  AssertEqual '/foo/bar', ale#lsp_linter#FindProjectRoot(0, g:linter)

Execute(PreProcess should complain when the project_root_callback valid is invalid):
  AssertThrows call ale#linter#PreProcess('testft', {
  \ 'name': 'x',
  \ 'lsp': 'socket',
  \ 'address': 'foo:123',
  \ 'language': 'x',
  \ 'project_root_callback': 0,
  \})
  AssertEqual '`project_root_callback` must be a callback if defined', g:vader_exception

Execute(PreProcess should complain about using initialization_options and initialization_options_callback together):
  let g:linter = {
  \ 'name': 'x',
  \ 'lsp': 'socket',
  \ 'address_callback': 'X',
  \ 'language': 'x',
  \ 'project_root_callback': 'x',
  \ 'initialization_options': 'x',
  \ 'initialization_options_callback': 'x',
  \}

  AssertThrows call ale#linter#PreProcess('testft', g:linter)
  AssertEqual 'Only one of `initialization_options` or `initialization_options_callback` should be set', g:vader_exception

Execute(PreProcess should throw when initialization_options_callback is not a callback):
  AssertThrows call ale#linter#PreProcess('testft', {
  \ 'name': 'foo',
  \ 'lsp': 'socket',
  \ 'address_callback': 'X',
  \ 'language': 'x',
  \ 'project_root_callback': 'x',
  \ 'initialization_options_callback': {},
  \})
  AssertEqual '`initialization_options_callback` must be a callback if defined', g:vader_exception

Execute(PreProcess should throw when initialization_options is not a Dictionary or callback):
  AssertThrows call ale#linter#PreProcess('testft', {
  \ 'name': 'foo',
  \ 'lsp': 'socket',
  \ 'address_callback': 'X',
  \ 'language': 'x',
  \ 'project_root_callback': 'x',
  \ 'initialization_options': 0,
  \})
  AssertEqual '`initialization_options` must be a String or Function if defined', g:vader_exception

Execute(PreProcess should accept initialization_options as a Dictionary):
  let g:linter = ale#linter#PreProcess('testft', {
  \ 'name': 'foo',
  \ 'lsp': 'socket',
  \ 'address_callback': 'X',
  \ 'language': 'x',
  \ 'project_root_callback': 'x',
  \ 'initialization_options': {'foo': v:true},
  \})

  AssertEqual {'foo': v:true}, ale#lsp_linter#GetOptions(0, g:linter)

Execute(PreProcess should accept initialization_options as a Funcref):
  let g:linter = ale#linter#PreProcess('testft', {
  \ 'name': 'foo',
  \ 'lsp': 'socket',
  \ 'address_callback': 'X',
  \ 'language': 'x',
  \ 'project_root_callback': 'x',
  \ 'initialization_options': {-> {'foo': v:true}},
  \})

  AssertEqual {'foo': v:true}, ale#lsp_linter#GetOptions(0, g:linter)

Execute(PreProcess should complain about using lsp_config and lsp_config_callback together):
  let g:linter = {
  \ 'name': 'x',
  \ 'lsp': 'socket',
  \ 'address_callback': 'X',
  \ 'language': 'x',
  \ 'project_root_callback': 'x',
  \ 'lsp_config': 'x',
  \ 'lsp_config_callback': 'x',
  \}

  AssertThrows call ale#linter#PreProcess('testft', g:linter)
  AssertEqual 'Only one of `lsp_config` or `lsp_config_callback` should be set', g:vader_exception

Execute(PreProcess should throw when lsp_config_callback is not a callback):
  AssertThrows call ale#linter#PreProcess('testft', {
  \ 'name': 'foo',
  \ 'lsp': 'socket',
  \ 'address_callback': 'X',
  \ 'language': 'x',
  \ 'project_root_callback': 'x',
  \ 'lsp_config_callback': {},
  \})
  AssertEqual '`lsp_config_callback` must be a callback if defined', g:vader_exception

Execute(PreProcess should accept LSP configuration options via lsp_config):
  let g:linter = {
  \ 'name': 'x',
  \ 'lsp': 'socket',
  \ 'address_callback': 'X',
  \ 'language_callback': 'x',
  \ 'project_root_callback': 'x',
  \ 'lsp_config': {'foo': 'bar'},
  \}

  AssertEqual {'foo': 'bar'}, ale#lsp_linter#GetConfig(0, g:linter)

Execute(PreProcess should accept LSP configuration options via lsp_config as a function):
  let g:linter = {
  \ 'name': 'x',
  \ 'lsp': 'socket',
  \ 'address_callback': 'X',
  \ 'language_callback': 'x',
  \ 'project_root_callback': 'x',
  \ 'lsp_config': {-> {'foo': 'bar'}},
  \}

  AssertEqual {'foo': 'bar'}, ale#lsp_linter#GetConfig(0, g:linter)

Execute(PreProcess should throw when lsp_config is not a Dictionary or Function):
  AssertThrows call ale#linter#PreProcess('testft', {
  \ 'name': 'foo',
  \ 'lsp': 'socket',
  \ 'address_callback': 'X',
  \ 'language': 'x',
  \ 'project_root_callback': 'x',
  \ 'lsp_config': 'x',
  \})
  AssertEqual '`lsp_config` must be a Dictionary or Function if defined', g:vader_exception