Before:
  Save g:ale_fixers
  Save &shell
  Save g:ale_enabled
  Save g:ale_fix_on_save
  Save g:ale_lint_on_save
  Save g:ale_echo_cursor

  silent! cd /testplugin/test

  let g:ale_enabled = 0
  let g:ale_echo_cursor = 0
  let g:ale_run_synchronously = 1
  let g:ale_fix_buffer_data = {}
  let g:ale_fixers = {
  \ 'testft': [],
  \}
  let &shell = '/bin/bash'

  function AddCarets(buffer, lines) abort
    " map() is applied to the original lines here.
    " This way, we can ensure that defensive copies are made.
    return map(a:lines, '''^'' . v:val')
  endfunction

  function AddDollars(buffer, lines) abort
    return map(a:lines, '''$'' . v:val')
  endfunction

  function DoNothing(buffer, lines) abort
    return 0
  endfunction

  function CatLine(buffer, lines) abort
    return {'command': 'cat - <(echo d)'}
  endfunction

  function CatLineOneArg(buffer) abort
    return {'command': 'cat - <(echo d)'}
  endfunction

  function ReplaceWithTempFile(buffer, lines) abort
    return {'command': 'echo x > %t', 'read_temporary_file': 1}
  endfunction

  function RemoveLastLine(buffer, lines) abort
    return ['a', 'b']
  endfunction

  function RemoveLastLineOneArg(buffer) abort
    return ['a', 'b']
  endfunction

  function! TestCallback(buffer, output)
    return [{'lnum': 1, 'col': 1, 'text': 'xxx'}]
  endfunction

  function! SetUpLinters()
    call ale#linter#Define('testft', {
    \ 'name': 'testlinter',
    \ 'callback': 'TestCallback',
    \ 'executable': 'true',
    \ 'command': 'true',
    \})
  endfunction

After:
  Restore
  unlet! g:ale_run_synchronously
  unlet! g:ale_emulate_job_failure
  unlet! b:ale_fixers
  delfunction AddCarets
  delfunction AddDollars
  delfunction DoNothing
  delfunction CatLine
  delfunction CatLineOneArg
  delfunction ReplaceWithTempFile
  delfunction RemoveLastLine
  delfunction RemoveLastLineOneArg
  delfunction TestCallback
  delfunction SetUpLinters
  call ale#fix#registry#ResetToDefaults()
  call ale#linter#Reset()

  setlocal buftype=nofile

  if filereadable('fix_test_file')
    call delete('fix_test_file')
  endif

  call setloclist(0, [])

  let g:ale_fix_buffer_data = {}

Given testft (A file with three lines):
  a
  b
  c

Execute(ALEFix should complain when there are no functions to call):
  AssertThrows ALEFix
  AssertEqual 'Vim(echoerr):No fixers have been defined. Try :ALEFixSuggest', g:vader_exception

Execute(ALEFix should apply simple functions):
  let g:ale_fixers.testft = ['AddCarets']
  ALEFix

Expect(The first function should be used):
  ^a
  ^b
  ^c

Execute(ALEFix should apply simple functions in a chain):
  let g:ale_fixers.testft = ['AddCarets', 'AddDollars']
  ALEFix

Expect(Both functions should be used):
  $^a
  $^b
  $^c

Execute(ALEFix should allow 0 to be returned to skip functions):
  let g:ale_fixers.testft = ['DoNothing', 'AddDollars']
  ALEFix

Expect(Only the second function should be applied):
  $a
  $b
  $c

Execute(ALEFix should allow commands to be run):
  let g:ale_fixers.testft = ['CatLine']
  ALEFix

Expect(An extra line should be added):
  a
  b
  c
  d

Execute(ALEFix should allow temporary files to be read):
  let g:ale_fixers.testft = ['ReplaceWithTempFile']
  ALEFix

Expect(The line we wrote to the temporary file should be used here):
  x

Execute(ALEFix should allow jobs and simple functions to be combined):
  let g:ale_fixers.testft = ['ReplaceWithTempFile', 'AddDollars']
  ALEFix

Expect(The lines from the temporary file should be modified):
  $x

Execute(ALEFix should send lines modified by functions to jobs):
  let g:ale_fixers.testft = ['AddDollars', 'CatLine']
  ALEFix

Expect(The lines should first be modified by the function, then the job):
  $a
  $b
  $c
  d

Execute(ALEFix should skip commands when jobs fail to run):
  let g:ale_emulate_job_failure = 1
  let g:ale_fixers.testft = ['CatLine', 'AddDollars']
  ALEFix

Expect(Only the second function should be applied):
  $a
  $b
  $c

Execute(ALEFix should handle strings for selecting a single function):
  let g:ale_fixers.testft = 'AddCarets'
  ALEFix

Expect(The first function should be used):
  ^a
  ^b
  ^c

Execute(ALEFix should use functions from the registry):
  call ale#fix#registry#Add('add_carets', 'AddCarets', [], 'Add some carets')
  let g:ale_fixers.testft = ['add_carets']
  ALEFix

Expect(The registry function should be used):
  ^a
  ^b
  ^c

Execute(ALEFix should be able to remove the last line for files):
  let g:ale_fixers.testft = ['RemoveLastLine']
  ALEFix

Expect(There should be only two lines):
  a
  b

Execute(ALEFix should accept funcrefs):
  let g:ale_fixers.testft = [function('RemoveLastLine')]
  ALEFix

Expect(There should be only two lines):
  a
  b

Execute(ALEFix should accept lambdas):
  if has('nvim')
    " NeoVim 0.1.7 can't interpret lambdas correctly, so just set the lines
    " to make the test pass.
    call setline(1, ['a', 'b', 'c', 'd'])
  else
    let g:ale_fixers.testft = [{buffer, lines -> lines + ['d']}]
    ALEFix
  endif

Expect(There should be an extra line):
  a
  b
  c
  d

Execute(ALEFix should user buffer-local fixer settings):
  let g:ale_fixers.testft = ['AddCarets', 'AddDollars']
  let b:ale_fixers = {'testft': ['RemoveLastLine']}
  ALEFix

Expect(There should be only two lines):
  a
  b

Given testft (A file with three lines):
  a
  b
  c

Execute(ALEFix should save files on the save event):
  let g:ale_fix_on_save = 1
  let g:ale_lint_on_save = 1
  let g:ale_enabled = 1

  noautocmd silent file fix_test_file
  call writefile(getline(1, '$'), 'fix_test_file')

  let g:ale_fixers.testft = ['AddDollars']

  " We have to set the buftype to empty so the file will be written.
  setlocal buftype=

  call SetUpLinters()
  call ale#events#SaveEvent(bufnr(''))

  " We should save the file.
  AssertEqual ['$a', '$b', '$c'], readfile('fix_test_file')
  Assert !&modified, 'The was marked as ''modified'''

  " We have run the linter.
  AssertEqual [{
  \   'bufnr': bufnr('%'),
  \   'lnum': 1,
  \   'vcol': 0,
  \   'col': 1,
  \   'text': 'xxx',
  \   'type': 'E',
  \   'nr': -1,
  \   'pattern': '',
  \   'valid': 1,
  \}], getloclist(0)

Expect(The buffer should be modified):
  $a
  $b
  $c

Given testft (A file with three lines):
  a
  b
  c

Execute(ALEFix should still lint with no linters to be applied):
  let g:ale_fix_on_save = 1
  let g:ale_lint_on_save = 1
  let g:ale_enabled = 1

  noautocmd silent file fix_test_file

  let g:ale_fixers.testft = []

  call SetUpLinters()
  call ale#events#SaveEvent(bufnr(''))

  Assert !filereadable('fix_test_file'), 'The file should not have been saved'

  " We have run the linter.
  AssertEqual [{
  \   'bufnr': bufnr('%'),
  \   'lnum': 1,
  \   'vcol': 0,
  \   'col': 1,
  \   'text': 'xxx',
  \   'type': 'E',
  \   'nr': -1,
  \   'pattern': '',
  \   'valid': 1,
  \}], getloclist(0)

Expect(The buffer should be the same):
  a
  b
  c

Execute(ALEFix should still lint when nothing was fixed on save):
  let g:ale_fix_on_save = 1
  let g:ale_lint_on_save = 1
  let g:ale_enabled = 1

  noautocmd silent file fix_test_file

  let g:ale_fixers.testft = ['DoNothing']

  call SetUpLinters()
  call ale#events#SaveEvent(bufnr(''))

  Assert !filereadable('fix_test_file'), 'The file should not have been saved'

  " We have run the linter.
  AssertEqual [{
  \   'bufnr': bufnr('%'),
  \   'lnum': 1,
  \   'vcol': 0,
  \   'col': 1,
  \   'text': 'xxx',
  \   'type': 'E',
  \   'nr': -1,
  \   'pattern': '',
  \   'valid': 1,
  \}], getloclist(0)

Expect(The buffer should be the same):
  a
  b
  c

Given testft (A file with three lines):
  a
  b
  c

Execute(ale#fix#InitBufferData() should set up the correct data):
  noautocmd silent file fix_test_file

  call ale#fix#InitBufferData(bufnr(''), 'save_file')

  AssertEqual {
  \ bufnr(''): {
  \   'temporary_directory_list': [],
  \   'vars': b:,
  \   'filename': simplify(getcwd() . '/fix_test_file'),
  \   'done': 0,
  \   'lines_before': ['a', 'b', 'c'],
  \   'should_save': 1,
  \ },
  \}, g:ale_fix_buffer_data

Execute(ALEFix simple functions should be able to accept one argument, the buffer):
  let g:ale_fixers.testft = ['RemoveLastLineOneArg']
  ALEFix

Expect(There should be only two lines):
  a
  b

Execute(ALEFix functions returning jobs should be able to accept one argument):
  let g:ale_fixers.testft = ['CatLine']
  ALEFix

Expect(An extra line should be added):
  a
  b
  c
  d