text: # Copyright (C) 2015-2020 ycmd contributors
  #
  # This file is part of ycmd.
  #
  # ycmd is free software: you can redistribute it and/or modify
  # it under the terms of the GNU General Public License as published by
  # the Free Software Foundation, either version 3 of the License, or
  # (at your option) any later version.
  #
  # ycmd is distributed in the hope that it will be useful,
  # but WITHOUT ANY WARRANTY; without even the implied warranty of
  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  # GNU General Public License for more details.
  #
  # You should have received a copy of the GNU General Public License
  # along with ycmd.  If not, see <http://www.gnu.org/licenses/>.
  
  from hamcrest import ( assert_that, calling, contains_exactly,
                         contains_inanyorder, contains_string, empty, equal_to,
                         has_entry, has_entries, raises, matches_regexp )
  from unittest.mock import patch
  from pprint import pprint
  from webtest import AppError
  import pytest
  import requests
  import os.path
  
  from ycmd import handlers
  from ycmd.completers.cpp.clang_completer import ( NO_DOCUMENTATION_MESSAGE,
                                                    PARSING_FILE_MESSAGE )
  from ycmd.tests.clang import ( MockCoreClangCompleter,
                                 PathToTestFile,
                                 SharedYcmd )
  from ycmd.tests.test_utils import ( BuildRequest,
                                      ErrorMatcher,
                                      ChunkMatcher,
                                      LocationMatcher,
                                      LineColMatcher )
  from ycmd.utils import ReadFile
  
  
  @SharedYcmd
  def Subcommands_DefinedSubcommands_test( app ):
    subcommands_data = BuildRequest( completer_target = 'cpp' )
    assert_that( app.post_json( '/defined_subcommands', subcommands_data ).json,
                 contains_inanyorder(
                   'ClearCompilationFlagCache',
                   'FixIt',
                   'GetDoc',
                   'GetDocImprecise',
                   'GetParent',
                   'GetType',
                   'GetTypeImprecise',
                   'GoTo',
                   'GoToDeclaration',
                   'GoToDefinition',
                   'GoToImprecise',
                   'GoToInclude' ) )
  
  
  @SharedYcmd
  def Subcommands_GoTo_ZeroBasedLineAndColumn_test( app ):
    contents = ReadFile( PathToTestFile(
      'GoTo_Clang_ZeroBasedLineAndColumn_test.cc' ) )
  
    goto_data = BuildRequest( completer_target = 'filetype_default',
                              command_arguments = [ 'GoToDefinition' ],
                              compilation_flags = [ '-x', 'c++' ],
                              line_num = 10,
                              column_num = 3,
                              contents = contents,
                              filetype = 'cpp' )
  
    assert_that( app.post_json( '/run_completer_command', goto_data ).json,
                 LocationMatcher( os.path.abspath( '/foo' ), 2, 8 ) )
  
  
  @SharedYcmd
  def Subcommands_GoTo_CUDA_test( app ):
    filepath = PathToTestFile( 'cuda', 'basic.cu' )
    contents = ReadFile( filepath )
  
    goto_data = BuildRequest( completer_target = 'filetype_default',
                              command_arguments = [ 'GoToDefinition' ],
                              compilation_flags = [ '-x', 'cuda' ],
                              line_num = 8,
                              column_num = 3,
                              filepath = filepath,
                              contents = contents,
                              filetype = 'cuda' )
  
    assert_that( app.post_json( '/run_completer_command', goto_data ).json,
                 LocationMatcher( filepath, 4, 17 ) )
  
  
  def RunGoToTest_all( app, filename, command, test ):
    contents = ReadFile( PathToTestFile( filename ) )
    common_request = {
      'completer_target' : 'filetype_default',
      'filepath'         : PathToTestFile( filename ),
      'command_arguments': command,
      'compilation_flags': [ '-x',
                             'c++' ],
      'line_num'         : 10,
      'column_num'       : 3,
      'contents'         : contents,
      'filetype'         : 'cpp'
    }
    common_response = {
      'filepath': os.path.abspath( PathToTestFile( filename ) ),
    }
  
    if 'extra_conf' in test:
      common_request.pop( 'compilation_flags' )
      app.post_json( '/load_extra_conf_file', {
        'filepath': PathToTestFile( *test[ 'extra_conf' ] ) } )
  
    request = common_request
    request.update( {
        'line_num'  : test[ 'request' ][ 0 ],
        'column_num': test[ 'request' ][ 1 ],
    } )
    response = common_response
    response.update( {
        'line_num'  : test[ 'response' ][ 0 ],
        'column_num': test[ 'response' ][ 1 ],
    } )
    if len( test[ 'response' ] ) > 2:
      response.update( {
        'filepath': PathToTestFile( test[ 'response' ][ 2 ] )
      } )
  
    goto_data = BuildRequest( **request )
  
    actual_response = app.post_json( '/run_completer_command', goto_data ).json
    pprint( actual_response )
    assert_that( response, equal_to( actual_response ) )
  
  
  @SharedYcmd
  def Subcommands_GoTo_all_test( app ):
    # GoToDeclaration
    tests = [
      # Local::x -> definition/declaration of x
      { 'request': [ 23, 21 ], 'response': [ 4,   9 ] },
      # Local::in_line -> definition/declaration of Local::in_line
      { 'request': [ 24, 26 ], 'response': [ 6,  10 ] },
      # Local -> definition/declaration of Local
      { 'request': [ 24, 16 ], 'response': [ 2,  11 ] },
      # Local::out_of_line -> declaration of Local::out_of_line
      { 'request': [ 25, 27 ], 'response': [ 11, 10 ] },
      # GoToDeclaration on definition of out_of_line moves to declaration
      { 'request': [ 14, 13 ], 'response': [ 11, 10 ] },
      # main -> declaration of main
      { 'request': [ 21,  7 ], 'response': [ 19,  5 ] },
      # Unicøde
      { 'request': [ 34,  8 ], 'response': [ 32, 26 ] },
      # Another_Unicøde
      { 'request': [ 36, 25 ], 'response': [ 32, 54 ] },
      { 'request': [ 38,  3 ], 'response': [ 36, 28 ] },
    ]
  
    for test in tests:
      RunGoToTest_all( app,
                       'GoTo_all_Clang_test.cc',
                       [ 'GoToDeclaration' ],
                       test )
  
    # GoToDefinition
    tests = [
      # Local::x -> declaration/definition of x
      { 'request': [ 23, 21 ], 'response': [ 4,   9 ] },
      # Local::in_line -> declaration/definition of Local::in_line
      { 'request': [ 24, 26 ], 'response': [ 6,  10 ] },
      # Local -> declaration/definition of Local
      { 'request': [ 24, 16 ], 'response': [ 2,  11 ] },
      # Local::out_of_line -> definition of Local::out_of_line
      { 'request': [ 25, 27 ], 'response': [ 14, 13 ] },
      # GoToDefinition on definition of out_of_line moves to itself
      { 'request': [ 14, 13 ], 'response': [ 14, 13 ] },
      # main -> definition of main (not declaration)
      { 'request': [ 21,  7 ], 'response': [ 21,  5 ] },
      # Unicøde
      { 'request': [ 34,  8 ], 'response': [ 32, 26 ] },
    ]
  
    for test in tests:
      RunGoToTest_all( app,
                       'GoTo_all_Clang_test.cc',
                       [ 'GoToDefinition' ],
                       test )
  
    # GoTo
    tests = [
      # Local::x -> declaration/definition of x
      { 'request': [ 23, 21 ], 'response': [ 4,   9 ] },
      # Local::in_line -> declaration/definition of Local::in_line
      { 'request': [ 24, 26 ], 'response': [ 6,  10 ] },
      # Local -> declaration/definition of Local
      { 'request': [ 24, 16 ], 'response': [ 2,  11 ] },
      # Local::out_of_line -> definition of Local::out_of_line
      { 'request': [ 25, 27 ], 'response': [ 14, 13 ] },
      # GoTo on definition of out_of_line moves to declaration
      { 'request': [ 14, 13 ], 'response': [ 11, 10 ] },
      # GoTo on declaration of out_of_line moves to definition
      { 'request': [ 11, 17 ], 'response': [ 14, 13 ] },
      # main -> definition of main
      { 'request': [ 21,  7 ], 'response': [ 19,  5 ] },
      # Unicøde
      { 'request': [ 34,  8 ], 'response': [ 32, 26 ] },
      # Another_Unicøde
      { 'request': [ 36, 25 ], 'response': [ 32, 54 ] },
      { 'request': [ 38,  3 ], 'response': [ 36, 28 ] },
    ]
  
    for test in tests:
      RunGoToTest_all( app,
                       'GoTo_all_Clang_test.cc',
                       [ 'GoTo' ],
                       test )
  
    # GoToImprecise - identical to GoTo
    tests = [
      # Local::x -> declaration/definition of x
      { 'request': [ 23, 21 ], 'response': [ 4,   9 ] },
      # Local::in_line -> declaration/definition of Local::in_line
      { 'request': [ 24, 26 ], 'response': [ 6,  10 ] },
      # Local -> declaration/definition of Local
      { 'request': [ 24, 16 ], 'response': [ 2,  11 ] },
      # Local::out_of_line -> definition of Local::out_of_line
      { 'request': [ 25, 27 ], 'response': [ 14, 13 ] },
      # GoToImprecise on definition of out_of_line moves to declaration
      { 'request': [ 14, 13 ], 'response': [ 11, 10 ] },
      # GoToImprecise on declaration of out_of_line moves to definition
      { 'request': [ 11, 17 ], 'response': [ 14, 13 ] },
      # main -> definition of main
      { 'request': [ 21,  7 ], 'response': [ 19,  5 ] },
      # Unicøde
      { 'request': [ 34,  8 ], 'response': [ 32, 26 ] },
      # Another_Unicøde
      { 'request': [ 36, 25 ], 'response': [ 32, 54 ] },
      { 'request': [ 38,  3 ], 'response': [ 36, 28 ] },
    ]
  
    for test in tests:
      RunGoToTest_all( app,
                       'GoTo_all_Clang_test.cc',
                       [ 'GoToImprecise' ],
                       test )
  
  
  @SharedYcmd
  def Subcommands_GoTo_all_Fail_test( app ):
    cursor_on_nothing = { 'request': [ 13, 1 ], 'response': [ 1, 1 ] }
    cursor_on_another_unicode = { 'request': [ 36, 17 ], 'response': [ 1, 1 ] }
    cursor_on_keyword = { 'request': [ 16, 6 ], 'response': [ 1, 1 ] }
  
    # GoToDeclaration
    assert_that(
      calling( RunGoToTest_all ).with_args( app,
                                            'GoTo_all_Clang_test.cc',
                                            [ 'GoToDeclaration' ],
                                            cursor_on_nothing ),
      raises( AppError, r'Can\\\'t jump to declaration.' ) )
    assert_that(
      calling( RunGoToTest_all ).with_args( app,
                                            'GoTo_all_Clang_test.cc',
                                            [ 'GoToDeclaration' ],
                                            cursor_on_keyword ),
      raises( AppError, r'Can\\\'t jump to declaration.' ) )
  
    # GoToDefinition
    assert_that(
      calling( RunGoToTest_all ).with_args( app,
                                            'GoTo_all_Clang_test.cc',
                                            [ 'GoToDefinition' ],
                                            cursor_on_nothing ),
      raises( AppError, r'Can\\\'t jump to definition.' ) )
    assert_that(
      calling( RunGoToTest_all ).with_args( app,
                                            'GoTo_all_Clang_test.cc',
                                            [ 'GoToDefinition' ],
                                            cursor_on_another_unicode ),
      raises( AppError, r'Can\\\'t jump to definition.' ) )
  
    # GoTo
    assert_that(
      calling( RunGoToTest_all ).with_args( app,
                                            'GoTo_all_Clang_test.cc',
                                            [ 'GoTo' ],
                                            cursor_on_nothing ),
      raises( AppError, r'Can\\\'t jump to definition or declaration.' ) )
    assert_that(
      calling( RunGoToTest_all ).with_args( app,
                                            'GoTo_all_Clang_test.cc',
                                            [ 'GoTo' ],
                                            cursor_on_keyword ),
      raises( AppError, r'Can\\\'t jump to definition or declaration.' ) )
  
    # GoToImprecise
    assert_that(
      calling( RunGoToTest_all ).with_args( app,
                                            'GoTo_all_Clang_test.cc',
                                            [ 'GoToImprecise' ],
                                            cursor_on_nothing ),
      raises( AppError, r'Can\\\'t jump to definition or declaration.' ) )
    assert_that(
      calling( RunGoToTest_all ).with_args( app,
                                            'GoTo_all_Clang_test.cc',
                                            [ 'GoToImprecise' ],
                                            cursor_on_keyword ),
      raises( AppError, r'Can\\\'t jump to definition or declaration.' ) )
  
  
  def RunGoToIncludeTest( app, command, test ):
    app.post_json(
      '/load_extra_conf_file',
      { 'filepath': PathToTestFile( 'test-include',
                                    '.ycm_extra_conf.py' ) } )
  
    filepath = PathToTestFile( 'test-include', 'main.cpp' )
    goto_data = BuildRequest( filepath = filepath,
                              filetype = 'cpp',
                              contents = ReadFile( filepath ),
                              command_arguments = [ command ],
                              line_num = test[ 'request' ][ 0 ],
                              column_num = test[ 'request' ][ 1 ] )
  
    response = {
      'filepath'   : PathToTestFile( 'test-include', test[ 'response' ] ),
      'line_num'   : 1,
      'column_num' : 1,
    }
  
    actual_response = app.post_json( '/run_completer_command', goto_data ).json
    pprint( actual_response )
    assert_that( response, equal_to( actual_response ) )
  
  
  @pytest.mark.parametrize( 'cmd', [ 'GoToInclude', 'GoTo', 'GoToImprecise' ] )
  @pytest.mark.parametrize( 'test', [
      { 'request': [ 1, 1 ], 'response': 'a.hpp' },
      { 'request': [ 2, 1 ], 'response': os.path.join( 'system', 'a.hpp' ) },
      { 'request': [ 3, 1 ], 'response': os.path.join( 'quote',  'b.hpp' ) },
      { 'request': [ 5, 1 ], 'response': os.path.join( 'system', 'c.hpp' ) },
      { 'request': [ 6, 1 ], 'response': os.path.join( 'system', 'c.hpp' ) },
      { 'request': [ 7, 1 ], 'response': os.path.join( 'Frameworks',
                                                       'OpenGL.framework',
                                                       'Headers',
                                                       'gl.h' ) },
      { 'request': [ 8, 1 ], 'response': os.path.join( 'Frameworks',
                                                       'OpenGL.framework',
                                                       'Headers',
                                                       'gl.h' ) },
    ] )
  @SharedYcmd
  def Subcommands_GoToInclude_test( app, cmd, test ):
    RunGoToIncludeTest( app, cmd, test )
  
  
  @SharedYcmd
  def Subcommands_GoToInclude_Fail_test( app ):
    test = { 'request': [ 4, 1 ], 'response': '' }
    assert_that(
      calling( RunGoToIncludeTest ).with_args( app,
                                               'GoToInclude', test ),
      raises( AppError, 'Include file not found.' ) )
    assert_that(
      calling( RunGoToIncludeTest ).with_args( app,
                                               'GoTo', test ),
      raises( AppError, 'Include file not found.' ) )
    assert_that(
      calling( RunGoToIncludeTest ).with_args( app,
                                               'GoToImprecise', test ),
      raises( AppError, 'Include file not found.' ) )
  
    test = { 'request': [ 9, 1 ], 'response': '' }
    assert_that(
      calling( RunGoToIncludeTest ).with_args( app,
                                               'GoToInclude', test ),
      raises( AppError, 'Not an include/import line.' ) )
    assert_that(
      calling( RunGoToIncludeTest ).with_args( app,
                                               'GoTo', test ),
      raises( AppError, r'Can\\\'t jump to definition or declaration.' ) )
    assert_that(
      calling( RunGoToIncludeTest ).with_args( app,
                                               'GoToImprecise', test ),
      raises( AppError, r'Can\\\'t jump to definition or declaration.' ) )
  
    # Unclosed #include statement.
    test = { 'request': [ 12, 13 ], 'response': '' }
    assert_that(
      calling( RunGoToIncludeTest ).with_args( app,
                                               'GoToInclude', test ),
      raises( AppError, 'Not an include/import line.' ) )
    assert_that(
      calling( RunGoToIncludeTest ).with_args( app,
                                               'GoTo', test ),
      raises( AppError, r'Can\\\'t jump to definition or declaration.' ) )
    assert_that(
      calling( RunGoToIncludeTest ).with_args( app,
                                               'GoToImprecise', test ),
      raises( AppError, r'Can\\\'t jump to definition or declaration.' ) )
  
  
  @SharedYcmd
  def Subcommands_GoTo_Unity_test( app ):
    RunGoToTest_all( app, 'unitya.cc', [ 'GoToDeclaration' ], {
      'request': [ 8, 21 ],
      'response': [ 1, 8, 'unity.cc' ],
      'extra_conf': [ '.ycm_extra_conf.py' ],
    } )
    RunGoToTest_all( app, 'unitya.cc', [ 'GoToInclude' ], {
      'request': [ 1, 14 ],
      'response': [ 1, 1, 'unity.h' ],
      'extra_conf': [ '.ycm_extra_conf.py' ],
    } )
  
  
  def RunGetSemanticTest( app, filepath, filetype, test, command ):
    contents = ReadFile( filepath )
    language = { 'cpp': 'c++', 'cuda': 'cuda' }
  
    # We use the -fno-delayed-template-parsing flag to not delay
    # parsing of templates on Windows.  This is the default on
    # other platforms.  See the _ExtraClangFlags function in
    # ycmd/completers/cpp/flags.py file for more information.
    common_args = {
      'completer_target' : 'filetype_default',
      'command_arguments': command,
      'compilation_flags': [ '-x',
                             language[ filetype ],
                             # C++11 flag is needed for lambda functions
                             '-std=c++11',
                             '-fno-delayed-template-parsing' ],
      'line_num'         : 10,
      'column_num'       : 3,
      'filepath'         : filepath,
      'contents'         : contents,
      'filetype'         : filetype
    }
  
    args = test[ 0 ]
    expected = test[ 1 ]
  
    if 'extra_conf' in args:
      common_args.pop( 'compilation_flags' )
      app.post_json( '/load_extra_conf_file', {
        'filepath': PathToTestFile( *args[ 'extra_conf' ] ) } )
      args.pop( 'extra_conf' )
  
    request = common_args
    request.update( args )
  
    request_data = BuildRequest( **request )
  
    response = app.post_json( '/run_completer_command', request_data ).json
    pprint( response )
    assert_that( response, has_entry( 'message', expected ) )
  
  
  @pytest.mark.parametrize( 'test', [
      # Basic pod types
      [ { 'line_num': 24, 'column_num':  3 }, 'Foo' ],
      [ { 'line_num':  1, 'column_num':  1 }, 'Internal error: '
                                              'cursor not valid' ],
      [ { 'line_num': 12, 'column_num':  2 }, 'Foo' ],
      [ { 'line_num': 12, 'column_num':  8 }, 'Foo' ],
      [ { 'line_num': 12, 'column_num':  9 }, 'Foo' ],
      [ { 'line_num': 12, 'column_num': 10 }, 'Foo' ],
      [ { 'line_num': 13, 'column_num':  3 }, 'int' ],
      [ { 'line_num': 13, 'column_num':  7 }, 'int' ],
      [ { 'line_num': 15, 'column_num':  7 }, 'char' ],
  
      # Function
      [ { 'line_num': 22, 'column_num':  2 }, 'int ()' ],
      [ { 'line_num': 22, 'column_num':  6 }, 'int ()' ],
  
      # Declared and canonical type
      # On Ns:: (Unknown)
      [ { 'line_num': 25, 'column_num':  3 }, 'Unknown type' ], # sic
      # On Type (Type)
      [ { 'line_num': 25, 'column_num':  8 }, 'Ns::Type => Ns::BasicType<char>' ],
      # On "a" (Ns::Type)
      [ { 'line_num': 25, 'column_num': 15 }, 'Ns::Type => Ns::BasicType<char>' ],
      [ { 'line_num': 26, 'column_num': 13 }, 'Ns::Type => Ns::BasicType<char>' ],
  
      # Cursor on decl for refs & pointers
      [ { 'line_num': 39, 'column_num':  3 }, 'Foo' ],
      [ { 'line_num': 39, 'column_num': 11 }, 'Foo &' ],
      [ { 'line_num': 39, 'column_num': 15 }, 'Foo' ],
      [ { 'line_num': 40, 'column_num':  3 }, 'Foo' ],
      [ { 'line_num': 40, 'column_num': 11 }, 'Foo *' ],
      [ { 'line_num': 40, 'column_num': 18 }, 'Foo' ],
      [ { 'line_num': 42, 'column_num':  3 }, 'const Foo &' ],
      [ { 'line_num': 42, 'column_num': 16 }, 'const Foo &' ],
      [ { 'line_num': 43, 'column_num':  3 }, 'const Foo *' ],
      [ { 'line_num': 43, 'column_num': 16 }, 'const Foo *' ],
  
      # Cursor on usage
      [ { 'line_num': 45, 'column_num': 13 }, 'const Foo' ],
      [ { 'line_num': 45, 'column_num': 19 }, 'const int' ],
      [ { 'line_num': 46, 'column_num': 13 }, 'const Foo *' ],
      [ { 'line_num': 46, 'column_num': 20 }, 'const int' ],
      [ { 'line_num': 47, 'column_num': 12 }, 'Foo' ],
      [ { 'line_num': 47, 'column_num': 17 }, 'int' ],
      [ { 'line_num': 48, 'column_num': 12 }, 'Foo *' ],
      [ { 'line_num': 48, 'column_num': 18 }, 'int' ],
  
      # Auto in declaration
      [ { 'line_num': 28, 'column_num':  3 }, 'Foo &' ],
      [ { 'line_num': 28, 'column_num': 11 }, 'Foo &' ],
      [ { 'line_num': 28, 'column_num': 18 }, 'Foo' ],
      [ { 'line_num': 29, 'column_num':  3 }, 'Foo *' ],
      [ { 'line_num': 29, 'column_num': 11 }, 'Foo *' ],
      [ { 'line_num': 29, 'column_num': 18 }, 'Foo' ],
      [ { 'line_num': 31, 'column_num':  3 }, 'const Foo &' ],
      [ { 'line_num': 31, 'column_num': 16 }, 'const Foo &' ],
      [ { 'line_num': 32, 'column_num':  3 }, 'const Foo *' ],
      [ { 'line_num': 32, 'column_num': 16 }, 'const Foo *' ],
  
      # Auto in usage
      [ { 'line_num': 34, 'column_num': 14 }, 'const Foo' ],
      [ { 'line_num': 34, 'column_num': 21 }, 'const int' ],
      [ { 'line_num': 35, 'column_num': 14 }, 'const Foo *' ],
      [ { 'line_num': 35, 'column_num': 22 }, 'const int' ],
      [ { 'line_num': 36, 'column_num': 13 }, 'Foo' ],
      [ { 'line_num': 36, 'column_num': 19 }, 'int' ],
      [ { 'line_num': 37, 'column_num': 13 }, 'Foo *' ],
      [ { 'line_num': 37, 'column_num': 20 }, 'int' ],
  
      # Unicode
      [ { 'line_num': 51, 'column_num': 13 }, 'Unicøde *' ],
  
      # Bound methods
      # On Win32, methods pick up an __attribute__((thiscall)) to annotate their
      # calling convention.  This shows up in the type, which isn't ideal, but
      # also prohibitively complex to try and strip out.
      [ { 'line_num': 53, 'column_num': 15 },
        matches_regexp( r'int \(int\)(?: __attribute__\(\(thiscall\)\))?' ) ],
      [ { 'line_num': 54, 'column_num': 18 },
        matches_regexp( r'int \(int\)(?: __attribute__\(\(thiscall\)\))?' ) ],
    ] )
  @pytest.mark.parametrize( 'cmd', [ 'GetType', 'GoToImprecise' ] )
  @SharedYcmd
  def Subcommands_GetType_test( app, cmd, test ):
    RunGetSemanticTest( app,
                        PathToTestFile( 'GetType_Clang_test.cc' ),
                        'cpp',
                        test,
                        [ 'GetType' ] )
  
  
  @SharedYcmd
  def SubCommands_GetType_CUDA_test( app ):
    test = [ { 'line_num': 8, 'column_num': 3, }, 'void ()' ]
    RunGetSemanticTest( app,
                        PathToTestFile( 'cuda', 'basic.cu' ),
                        'cuda',
                        test,
                        [ 'GetType' ] )
  
  
  @SharedYcmd
  def SubCommands_GetType_Unity_test( app ):
    test = [
      {
        'line_num': 10,
        'column_num': 25,
        'extra_conf': [ '.ycm_extra_conf.py' ]
      },
      'int'
    ]
    RunGetSemanticTest( app,
                        PathToTestFile( 'unitya.cc' ),
                        'cpp',
                        test,
                        [ 'GetType' ] )
  
  
  @pytest.mark.parametrize( 'test', [
      [ { 'line_num':  1,  'column_num':  1 }, 'Internal error: '
                                              'cursor not valid' ],
      [ { 'line_num':  2,  'column_num':  8 },
        PathToTestFile( 'GetParent_Clang_test.cc' ) ],
  
      # The reported scope does not include parents
      [ { 'line_num':  3,  'column_num': 11 }, 'A' ],
      [ { 'line_num':  4,  'column_num': 13 }, 'B' ],
      [ { 'line_num':  5,  'column_num': 13 }, 'B' ],
      [ { 'line_num':  9,  'column_num': 17 }, 'do_z_inline()' ],
      [ { 'line_num': 15,  'column_num': 22 }, 'do_anything(T &)' ],
      [ { 'line_num': 19,  'column_num':  9 }, 'A' ],
      [ { 'line_num': 20,  'column_num':  9 }, 'A' ],
      [ { 'line_num': 22,  'column_num': 12 }, 'A' ],
      [ { 'line_num': 23,  'column_num':  5 }, 'do_Z_inline()' ],
      [ { 'line_num': 24,  'column_num': 12 }, 'do_Z_inline()' ],
      [ { 'line_num': 28,  'column_num': 14 }, 'A' ],
  
      [ { 'line_num': 34,  'column_num':  1 }, 'do_anything(T &)' ],
      [ { 'line_num': 39,  'column_num':  1 }, 'do_x()' ],
      [ { 'line_num': 44,  'column_num':  1 }, 'do_y()' ],
      [ { 'line_num': 49,  'column_num':  1 }, 'main()' ],
  
      # Lambdas report the name of the variable
      [ { 'line_num': 49,  'column_num': 14 }, 'l' ],
      [ { 'line_num': 50,  'column_num': 19 }, 'l' ],
      [ { 'line_num': 51,  'column_num': 16 }, 'main()' ],
    ] )
  @SharedYcmd
  def Subcommands_GetParent_test( app, test ):
    RunGetSemanticTest( app,
                        PathToTestFile( 'GetParent_Clang_test.cc' ),
                        'cpp',
                        test,
                        [ 'GetParent' ] )
  
  
  def RunFixItTest( app, line, column, lang, file_path, check ):
    contents = ReadFile( file_path )
  
    language_options = {
      'cpp11': {
        'compilation_flags': [ '-x',
                               'c++',
                               '-std=c++11',
                               '-Wall',
                               '-Wextra',
                               '-pedantic' ],
        'filetype'         : 'cpp',
      },
      'cuda': {
        'compilation_flags': [ '-x',
                               'cuda',
                               '-std=c++11',
                               '-Wall',
                               '-Wextra',
                               '-pedantic' ],
        'filetype'         : 'cuda',
      },
      'objective-c': {
        'compilation_flags': [ '-x',
                               'objective-c',
                               '-Wall',
                               '-Wextra' ],
        'filetype'         : 'objc',
      },
    }
  
    # Build the command arguments from the standard ones and the language-specific
    # arguments.
    args = {
      'completer_target' : 'filetype_default',
      'contents'         : contents,
      'filepath'         : file_path,
      'command_arguments': [ 'FixIt' ],
      'line_num'         : line,
      'column_num'       : column,
    }
    args.update( language_options[ lang ] )
  
    # Get the fixes for the file.
    event_data = BuildRequest( **args )
  
    results = app.post_json( '/run_completer_command', event_data ).json
  
    pprint( results )
    check( results )
  
  
  def FixIt_Check_cpp11_Ins( results ):
    # First fixit
    #   switch(A()) { // expected-error{{explicit conversion to}}
    assert_that( results, has_entries( {
      'fixits': contains_exactly( has_entries( {
        'chunks': contains_exactly(
          has_entries( {
            'replacement_text': equal_to( 'static_cast<int>(' ),
            'range': has_entries( {
              'start': has_entries( { 'line_num': 16, 'column_num': 10 } ),
              'end'  : has_entries( { 'line_num': 16, 'column_num': 10 } ),
            } ),
          } ),
          has_entries( {
            'replacement_text': equal_to( ')' ),
            'range': has_entries( {
              'start': has_entries( { 'line_num': 16, 'column_num': 13 } ),
              'end'  : has_entries( { 'line_num': 16, 'column_num': 13 } ),
            } ),
          } )
        ),
        'location': has_entries( { 'line_num': 16, 'column_num': 3 } )
      } ) )
    } ) )
  
  
  def FixIt_Check_cpp11_InsMultiLine( results ):
    # Similar to FixIt_Check_cpp11_1 but inserts split across lines
    #
    assert_that( results, has_entries( {
      'fixits': contains_exactly( has_entries( {
        'chunks': contains_exactly(
          has_entries( {
            'replacement_text': equal_to( 'static_cast<int>(' ),
            'range': has_entries( {
              'start': has_entries( { 'line_num': 26, 'column_num': 7 } ),
              'end'  : has_entries( { 'line_num': 26, 'column_num': 7 } ),
            } ),
          } ),
          has_entries( {
            'replacement_text': equal_to( ')' ),
            'range': has_entries( {
              'start': has_entries( { 'line_num': 28, 'column_num': 2 } ),
              'end'  : has_entries( { 'line_num': 28, 'column_num': 2 } ),
            } ),
          } )
        ),
        'location': has_entries( { 'line_num': 25, 'column_num': 3 } )
      } ) )
    } ) )
  
  
  def FixIt_Check_cpp11_Del( results ):
    # Removal of ::
    assert_that( results, has_entries( {
      'fixits': contains_exactly( has_entries( {
        'chunks': contains_exactly(
          has_entries( {
            'replacement_text': equal_to( '' ),
            'range': has_entries( {
              'start': has_entries( { 'line_num': 35, 'column_num': 7 } ),
              'end'  : has_entries( { 'line_num': 35, 'column_num': 9 } ),
            } ),
          } )
        ),
        'location': has_entries( { 'line_num': 35, 'column_num': 7 } )
      } ) )
    } ) )
  
  
  def FixIt_Check_cpp11_Repl( results ):
    assert_that( results, has_entries( {
      'fixits': contains_exactly( has_entries( {
        'chunks': contains_exactly(
          has_entries( {
            'replacement_text': equal_to( 'foo' ),
            'range': has_entries( {
              'start': has_entries( { 'line_num': 40, 'column_num': 6 } ),
              'end'  : has_entries( { 'line_num': 40, 'column_num': 9 } ),
            } ),
          } )
        ),
        'location': has_entries( { 'line_num': 40, 'column_num': 6 } )
      } ) )
    } ) )
  
  
  def FixIt_Check_cpp11_DelAdd( results ):
    assert_that( results, has_entries( {
      'fixits': contains_exactly( has_entries( {
        'chunks': contains_exactly(
          has_entries( {
            'replacement_text': equal_to( '' ),
            'range': has_entries( {
              'start': has_entries( { 'line_num': 48, 'column_num': 3 } ),
              'end'  : has_entries( { 'line_num': 48, 'column_num': 4 } ),
            } ),
          } ),
          has_entries( {
            'replacement_text': equal_to( '~' ),
            'range': has_entries( {
              'start': has_entries( { 'line_num': 48, 'column_num': 9 } ),
              'end'  : has_entries( { 'line_num': 48, 'column_num': 9 } ),
            } ),
          } ),
        ),
        'location': has_entries( { 'line_num': 48, 'column_num': 3 } )
      } ) )
    } ) )
  
  
  def FixIt_Check_objc( results ):
    assert_that( results, has_entries( {
      'fixits': contains_exactly( has_entries( {
        'chunks': contains_exactly(
          has_entries( {
            'replacement_text': equal_to( 'id' ),
            'range': has_entries( {
              'start': has_entries( { 'line_num': 5, 'column_num': 3 } ),
              'end'  : has_entries( { 'line_num': 5, 'column_num': 3 } ),
            } ),
          } )
        ),
        'location': has_entries( { 'line_num': 5, 'column_num': 3 } )
      } ) )
    } ) )
  
  
  def FixIt_Check_objc_NoFixIt( results ):
    # and finally, a warning with no fixits
    assert_that( results, equal_to( { 'fixits': [] } ) )
  
  
  def FixIt_Check_cpp11_MultiFirst( results ):
    assert_that( results, has_entries( {
      'fixits': contains_exactly(
        # first fix-it at 54,16
        has_entries( {
          'chunks': contains_exactly(
            has_entries( {
              'replacement_text': equal_to( 'foo' ),
              'range': has_entries( {
                'start': has_entries( { 'line_num': 54, 'column_num': 16 } ),
                'end'  : has_entries( { 'line_num': 54, 'column_num': 19 } ),
              } ),
            } )
          ),
          'location': has_entries( { 'line_num': 54, 'column_num': 16 } )
        } ),
        # second fix-it at 54,52
        has_entries( {
          'chunks': contains_exactly(
            has_entries( {
              'replacement_text': equal_to( '' ),
              'range': has_entries( {
                'start': has_entries( { 'line_num': 54, 'column_num': 52 } ),
                'end'  : has_entries( { 'line_num': 54, 'column_num': 53 } ),
              } ),
            } ),
            has_entries( {
              'replacement_text': equal_to( '~' ),
              'range': has_entries( {
                'start': has_entries( { 'line_num': 54, 'column_num': 58 } ),
                'end'  : has_entries( { 'line_num': 54, 'column_num': 58 } ),
              } ),
            } ),
          ),
          'location': has_entries( { 'line_num': 54, 'column_num': 52 } )
        } )
      )
    } ) )
  
  
  def FixIt_Check_cpp11_MultiSecond( results ):
    assert_that( results, has_entries( {
      'fixits': contains_exactly(
        # second fix-it at 54,52
        has_entries( {
          'chunks': contains_exactly(
            has_entries( {
              'replacement_text': equal_to( '' ),
              'range': has_entries( {
                'start': has_entries( { 'line_num': 54, 'column_num': 52 } ),
                'end'  : has_entries( { 'line_num': 54, 'column_num': 53 } ),
              } ),
            } ),
            has_entries( {
              'replacement_text': equal_to( '~' ),
              'range': has_entries( {
                'start': has_entries( { 'line_num': 54, 'column_num': 58 } ),
                'end'  : has_entries( { 'line_num': 54, 'column_num': 58 } ),
              } ),
            } ),
          ),
          'location': has_entries( { 'line_num': 54, 'column_num': 52 } )
        } ),
        # first fix-it at 54,16
        has_entries( {
          'chunks': contains_exactly(
            has_entries( {
              'replacement_text': equal_to( 'foo' ),
              'range': has_entries( {
                'start': has_entries( { 'line_num': 54, 'column_num': 16 } ),
                'end'  : has_entries( { 'line_num': 54, 'column_num': 19 } ),
              } ),
            } )
          ),
          'location': has_entries( { 'line_num': 54, 'column_num': 16 } )
        } )
      )
    } ) )
  
  
  def FixIt_Check_unicode_Ins( results ):
    assert_that( results, has_entries( {
      'fixits': contains_exactly( has_entries( {
        'chunks': contains_exactly(
          has_entries( {
            'replacement_text': equal_to( ';' ),
            'range': has_entries( {
              'start': has_entries( { 'line_num': 21, 'column_num': 39 } ),
              'end'  : has_entries( { 'line_num': 21, 'column_num': 39 } ),
            } ),
          } )
        ),
        'location': has_entries( { 'line_num': 21, 'column_num': 39 } )
      } ) )
    } ) )
  
  
  def FixIt_Check_cpp11_Note( results ):
    assert_that( results, has_entries( {
      'fixits': contains_exactly(
        # First note: put parens around it
        has_entries( {
          'text': contains_string( 'parentheses around the assignment' ),
          'chunks': contains_exactly(
            ChunkMatcher( '(',
                          LineColMatcher( 59, 8 ),
                          LineColMatcher( 59, 8 ) ),
            ChunkMatcher( ')',
                          LineColMatcher( 61, 12 ),
                          LineColMatcher( 61, 12 ) )
          ),
          'location': LineColMatcher( 60, 8 ),
        } ),
  
        # Second note: change to ==
        has_entries( {
          'text': contains_string( '==' ),
          'chunks': contains_exactly(
            ChunkMatcher( '==',
                          LineColMatcher( 60, 8 ),
                          LineColMatcher( 60, 9 ) )
          ),
          'location': LineColMatcher( 60, 8 ),
        } )
      )
    } ) )
  
  
  def FixIt_Check_cpp11_SpellCheck( results ):
    assert_that( results, has_entries( {
      'fixits': contains_exactly(
        # Change to SpellingIsNotMyStrongPoint
        has_entries( {
          'text': contains_string( "did you mean 'SpellingIsNotMyStrongPoint'" ),
          'chunks': contains_exactly(
            ChunkMatcher( 'SpellingIsNotMyStrongPoint',
                          LineColMatcher( 72, 9 ),
                          LineColMatcher( 72, 35 ) )
          ),
          'location': LineColMatcher( 72, 9 ),
        } ) )
    } ) )
  
  
  def FixIt_Check_cuda( results ):
    assert_that( results, has_entries( {
      'fixits': contains_exactly(
        has_entries( {
          'text': contains_string(
             "error: kernel function type 'int ()' must have void " ),
          'chunks': contains_exactly(
            ChunkMatcher( 'void',
                          LineColMatcher( 3, 12 ),
                          LineColMatcher( 3, 15 ) )
          ),
          'location': LineColMatcher( 3, 12 ),
        } ) )
    } ) )
  
  
  @pytest.mark.parametrize( 'test', [
      # L
      # i   C
      # n   o
      # e   l   Lang     File,  Checker
      [ 16, 0,  'cpp11', PathToTestFile( 'FixIt_Clang_cpp11.cpp' ),
        FixIt_Check_cpp11_Ins ],
      [ 16, 1,  'cpp11', PathToTestFile( 'FixIt_Clang_cpp11.cpp' ),
        FixIt_Check_cpp11_Ins ],
      [ 16, 10, 'cpp11', PathToTestFile( 'FixIt_Clang_cpp11.cpp' ),
        FixIt_Check_cpp11_Ins ],
      [ 25, 14, 'cpp11', PathToTestFile( 'FixIt_Clang_cpp11.cpp' ),
        FixIt_Check_cpp11_InsMultiLine ],
      [ 25, 0,  'cpp11', PathToTestFile( 'FixIt_Clang_cpp11.cpp' ),
        FixIt_Check_cpp11_InsMultiLine ],
      [ 35, 7,  'cpp11', PathToTestFile( 'FixIt_Clang_cpp11.cpp' ),
        FixIt_Check_cpp11_Del ],
      [ 40, 6,  'cpp11', PathToTestFile( 'FixIt_Clang_cpp11.cpp' ),
        FixIt_Check_cpp11_Repl ],
      [ 48, 3,  'cpp11', PathToTestFile( 'FixIt_Clang_cpp11.cpp' ),
        FixIt_Check_cpp11_DelAdd ],
  
      [ 5, 3,   'objective-c', PathToTestFile( 'FixIt_Clang_objc.m' ),
        FixIt_Check_objc ],
      [ 7, 1,   'objective-c', PathToTestFile( 'FixIt_Clang_objc.m' ),
        FixIt_Check_objc_NoFixIt ],
  
      [ 3, 12,  'cuda', PathToTestFile( 'cuda', 'fixit_test.cu' ),
        FixIt_Check_cuda ],
  
      # multiple errors on a single line; both with fixits
      [ 54, 15, 'cpp11', PathToTestFile( 'FixIt_Clang_cpp11.cpp' ),
        FixIt_Check_cpp11_MultiFirst ],
      [ 54, 16, 'cpp11', PathToTestFile( 'FixIt_Clang_cpp11.cpp' ),
        FixIt_Check_cpp11_MultiFirst ],
      [ 54, 16, 'cpp11', PathToTestFile( 'FixIt_Clang_cpp11.cpp' ),
        FixIt_Check_cpp11_MultiFirst ],
      [ 54, 17, 'cpp11', PathToTestFile( 'FixIt_Clang_cpp11.cpp' ),
        FixIt_Check_cpp11_MultiFirst ],
      [ 54, 18, 'cpp11', PathToTestFile( 'FixIt_Clang_cpp11.cpp' ),
        FixIt_Check_cpp11_MultiFirst ],
  
      # should put closest fix-it first?
      [ 54, 51, 'cpp11', PathToTestFile( 'FixIt_Clang_cpp11.cpp' ),
        FixIt_Check_cpp11_MultiSecond ],
      [ 54, 52, 'cpp11', PathToTestFile( 'FixIt_Clang_cpp11.cpp' ),
        FixIt_Check_cpp11_MultiSecond ],
      [ 54, 53, 'cpp11', PathToTestFile( 'FixIt_Clang_cpp11.cpp' ),
        FixIt_Check_cpp11_MultiSecond ],
  
      # unicode in line for fixit
      [ 21, 16, 'cpp11', PathToTestFile( 'unicode.cc' ),
        FixIt_Check_unicode_Ins ],
  
      # FixIt attached to a "child" diagnostic (i.e. a Note)
      [ 60, 1,  'cpp11', PathToTestFile( 'FixIt_Clang_cpp11.cpp' ),
        FixIt_Check_cpp11_Note ],
  
      # FixIt due to forced spell checking
      [ 72, 9,  'cpp11', PathToTestFile( 'FixIt_Clang_cpp11.cpp' ),
        FixIt_Check_cpp11_SpellCheck ],
    ] )
  @SharedYcmd
  def Subcommands_FixIt_all_test( app, test ):
    RunFixItTest( app, test[ 0 ], test[ 1 ], test[ 2 ], test[ 3 ], test[ 4 ] )
  
  
  @SharedYcmd
  def Subcommands_FixIt_Unity_test( app ):
    file_path = PathToTestFile( 'unitya.cc' )
    args = {
      'filetype'         : 'cpp',
      'completer_target' : 'filetype_default',
      'contents'         : ReadFile( file_path ),
      'filepath'         : file_path,
      'command_arguments': [ 'FixIt' ],
      'line_num'         : 11,
      'column_num'       : 17,
    }
    app.post_json( '/load_extra_conf_file', {
      'filepath': PathToTestFile( '.ycm_extra_conf.py' ),
    } )
  
    # Get the fixes for the file.
    event_data = BuildRequest( **args )
  
    results = app.post_json( '/run_completer_command', event_data ).json
  
    pprint( results )
    assert_that( results, has_entries( {
      'fixits': contains_exactly( has_entries( {
        'text': contains_string( "expected ';' after expression" ),
        'chunks': contains_exactly(
          ChunkMatcher( ';',
                        LocationMatcher( file_path, 11, 18 ),
                        LocationMatcher( file_path, 11, 18 ) ),
        ),
        'location': LocationMatcher( file_path, 11, 18 ),
      } ) )
    } ) )
  
  
  @SharedYcmd
  def Subcommands_FixIt_UnityDifferentFile_test( app ):
    # This checks that we only return FixIt for the requested file, not a fixit on
    # the same line in a different file
    file_path = PathToTestFile( 'unity.cc' )
    args = {
      'filetype'         : 'cpp',
      'completer_target' : 'filetype_default',
      'contents'         : ReadFile( file_path ),
      'filepath'         : file_path,
      'command_arguments': [ 'FixIt' ],
      'line_num'         : 11,
      'column_num'       : 17,
    }
    app.post_json( '/load_extra_conf_file', {
      'filepath': PathToTestFile( '.ycm_extra_conf.py' ),
    } )
  
    # Get the fixes for the file.
    event_data = BuildRequest( **args )
  
    results = app.post_json( '/run_completer_command', event_data ).json
  
    pprint( results )
    assert_that( results, has_entries( {
      'fixits': empty()
    } ) )
  
  
  @SharedYcmd
  def Subcommands_FixIt_NonExistingFile_test( app ):
    # This checks that FixIt is working for a non-existing file and that the path
    # is properly normalized ('.' and '..' are removed from the path).
    file_path = PathToTestFile( 'non_existing_dir', '..', '.', 'non_existing.cc' )
    normal_file_path = PathToTestFile( 'non_existing.cc' )
    args = {
      'filetype'         : 'cpp',
      'completer_target' : 'filetype_default',
      'contents'         : 'int test',
      'filepath'         : file_path,
      'command_arguments': [ 'FixIt' ],
      'line_num'         : 1,
      'column_num'       : 1,
    }
    app.post_json( '/load_extra_conf_file', {
      'filepath': PathToTestFile( '.ycm_extra_conf.py' ),
    } )
  
    # Get the fixes for the file.
    event_data = BuildRequest( **args )
  
    results = app.post_json( '/run_completer_command', event_data ).json
  
    pprint( results )
    assert_that( results, has_entries( {
      'fixits': contains_exactly( has_entries( {
        'text': contains_string( "expected ';' after top level declarator" ),
        'chunks': contains_exactly(
          ChunkMatcher( ';',
                        LocationMatcher( normal_file_path, 1, 9 ),
                        LocationMatcher( normal_file_path, 1, 9 ) ),
        ),
        'location': LocationMatcher( normal_file_path, 1, 9 ),
      } ) )
    } ) )
  
  
  @SharedYcmd
  def Subcommands_GetDoc_Variable_test( app ):
    filepath = PathToTestFile( 'GetDoc_Clang.cc' )
    contents = ReadFile( filepath )
  
    event_data = BuildRequest( filepath = filepath,
                               filetype = 'cpp',
                               compilation_flags = [ '-x', 'c++' ],
                               line_num = 70,
                               column_num = 24,
                               contents = contents,
                               command_arguments = [ 'GetDoc' ] )
  
    response = app.post_json( '/run_completer_command', event_data ).json
  
    pprint( response )
  
    assert_that( response, has_entry(
      'detailed_info', """\
  char a_global_variable
  This really is a global variable.
  Type: char
  Name: a_global_variable
  ---
  This really is a global variable.
  
  The first line of comment is the brief.""" ) )
  
  
  @SharedYcmd
  def Subcommands_GetDoc_Method_test( app ):
    filepath = PathToTestFile( 'GetDoc_Clang.cc' )
    contents = ReadFile( filepath )
  
    event_data = BuildRequest( filepath = filepath,
                               filetype = 'cpp',
                               compilation_flags = [ '-x', 'c++' ],
                               line_num = 22,
                               column_num = 13,
                               contents = contents,
                               command_arguments = [ 'GetDoc' ] )
  
    response = app.post_json( '/run_completer_command', event_data ).json
  
    pprint( response )
  
    assert_that( response, has_entry(
      'detailed_info', """\
  char with_brief()
  brevity is for suckers
  Type: char ()
  Name: with_brief
  ---
  
  This is not the brief.
  
  \\brief brevity is for suckers
  
  This is more information
  """ ) )
  
  
  @SharedYcmd
  def Subcommands_GetDoc_Namespace_test( app ):
    filepath = PathToTestFile( 'GetDoc_Clang.cc' )
    contents = ReadFile( filepath )
  
    event_data = BuildRequest( filepath = filepath,
                               filetype = 'cpp',
                               compilation_flags = [ '-x', 'c++' ],
                               line_num = 65,
                               column_num = 14,
                               contents = contents,
                               command_arguments = [ 'GetDoc' ] )
  
    response = app.post_json( '/run_completer_command', event_data ).json
  
    pprint( response )
  
    assert_that( response, has_entry(
      'detailed_info', """\
  namespace Test {}
  This is a test namespace
  Type: 
  Name: Test
  ---
  This is a test namespace""" ) ) # noqa
  
  
  @SharedYcmd
  def Subcommands_GetDoc_Undocumented_test( app ):
    filepath = PathToTestFile( 'GetDoc_Clang.cc' )
    contents = ReadFile( filepath )
  
    event_data = BuildRequest( filepath = filepath,
                               filetype = 'cpp',
                               compilation_flags = [ '-x', 'c++' ],
                               line_num = 81,
                               column_num = 17,
                               contents = contents,
                               command_arguments = [ 'GetDoc' ] )
  
    response = app.post_json( '/run_completer_command',
                              event_data,
                              expect_errors = True )
  
    assert_that( response.status_code,
                 equal_to( requests.codes.internal_server_error ) )
  
    assert_that( response.json,
                 ErrorMatcher( ValueError, NO_DOCUMENTATION_MESSAGE ) )
  
  
  @SharedYcmd
  def Subcommands_GetDoc_NoCursor_test( app ):
    filepath = PathToTestFile( 'GetDoc_Clang.cc' )
    contents = ReadFile( filepath )
  
    event_data = BuildRequest( filepath = filepath,
                               filetype = 'cpp',
                               compilation_flags = [ '-x', 'c++' ],
                               line_num = 1,
                               column_num = 1,
                               contents = contents,
                               command_arguments = [ 'GetDoc' ] )
  
    response = app.post_json( '/run_completer_command',
                              event_data,
                              expect_errors = True )
  
    assert_that( response.status_code,
                 equal_to( requests.codes.internal_server_error ) )
  
    assert_that( response.json,
                 ErrorMatcher( ValueError, NO_DOCUMENTATION_MESSAGE ) )
  
  
  @SharedYcmd
  def Subcommands_GetDoc_SystemHeaders_test( app ):
    app.post_json( '/load_extra_conf_file', {
      'filepath': PathToTestFile( 'get_doc', '.ycm_extra_conf.py' ) } )
  
    filepath = PathToTestFile( 'get_doc', 'test.cpp' )
    contents = ReadFile( filepath )
  
    event_data = BuildRequest( filepath = filepath,
                               filetype = 'cpp',
                               line_num = 4,
                               column_num = 7,
                               contents = contents,
                               command_arguments = [ 'GetDoc' ] )
  
    response = app.post_json( '/run_completer_command', event_data ).json
  
    assert_that( response,
                 has_entry( 'detailed_info', """\
  int test()
  This is a function.
  Type: int ()
  Name: test
  ---
  
  \\brief This is a function.
  
  This function is defined in a system header.
  """ ) )
  
  
  # Following tests repeat the tests above, but without re-parsing the file
  @SharedYcmd
  def Subcommands_GetDocImprecise_Variable_test( app ):
    filepath = PathToTestFile( 'GetDoc_Clang.cc' )
    contents = ReadFile( filepath )
  
    app.post_json( '/event_notification',
                   BuildRequest( filepath = filepath,
                                 filetype = 'cpp',
                                 compilation_flags = [ '-x', 'c++' ],
                                 contents = contents,
                                 event_name = 'FileReadyToParse' ) )
  
    event_data = BuildRequest( filepath = filepath,
                               filetype = 'cpp',
                               compilation_flags = [ '-x', 'c++' ],
                               line_num = 70,
                               column_num = 24,
                               contents = contents,
                               command_arguments = [ 'GetDocImprecise' ] )
  
    response = app.post_json( '/run_completer_command', event_data ).json
  
    pprint( response )
  
    assert_that( response, has_entry(
      'detailed_info', """\
  char a_global_variable
  This really is a global variable.
  Type: char
  Name: a_global_variable
  ---
  This really is a global variable.
  
  The first line of comment is the brief.""" ) )
  
  
  @SharedYcmd
  def Subcommands_GetDocImprecise_Method_test( app ):
    filepath = PathToTestFile( 'GetDoc_Clang.cc' )
    contents = ReadFile( filepath )
  
    app.post_json(
      '/event_notification',
      BuildRequest( filepath = filepath,
                    filetype = 'cpp',
                    compilation_flags = [ '-x', 'c++' ],
                    contents = contents,
                    event_name = 'FileReadyToParse' )
    )
  
    event_data = BuildRequest( filepath = filepath,
                               filetype = 'cpp',
                               compilation_flags = [ '-x', 'c++' ],
                               line_num = 22,
                               column_num = 13,
                               contents = contents,
                               command_arguments = [ 'GetDocImprecise' ] )
  
    response = app.post_json( '/run_completer_command', event_data ).json
  
    pprint( response )
  
    assert_that( response, has_entry(
      'detailed_info', """\
  char with_brief()
  brevity is for suckers
  Type: char ()
  Name: with_brief
  ---
  
  This is not the brief.
  
  \\brief brevity is for suckers
  
  This is more information
  """ ) )
  
  
  @SharedYcmd
  def Subcommands_GetDocImprecise_Namespace_test( app ):
    filepath = PathToTestFile( 'GetDoc_Clang.cc' )
    contents = ReadFile( filepath )
  
    app.post_json(
      '/event_notification',
      BuildRequest( filepath = filepath,
                    filetype = 'cpp',
                    compilation_flags = [ '-x', 'c++' ],
                    contents = contents,
                    event_name = 'FileReadyToParse' )
    )
  
    event_data = BuildRequest( filepath = filepath,
                               filetype = 'cpp',
                               compilation_flags = [ '-x', 'c++' ],
                               line_num = 65,
                               column_num = 14,
                               contents = contents,
                               command_arguments = [ 'GetDocImprecise' ] )
  
    response = app.post_json( '/run_completer_command', event_data ).json
  
    pprint( response )
  
    assert_that( response, has_entry(
      'detailed_info', """\
  namespace Test {}
  This is a test namespace
  Type: 
  Name: Test
  ---
  This is a test namespace""" ) ) # noqa
  
  
  @SharedYcmd
  def Subcommands_GetDocImprecise_Undocumented_test( app ):
    filepath = PathToTestFile( 'GetDoc_Clang.cc' )
    contents = ReadFile( filepath )
  
    app.post_json(
      '/event_notification',
      BuildRequest( filepath = filepath,
                    filetype = 'cpp',
                    compilation_flags = [ '-x', 'c++' ],
                    contents = contents,
                    event_name = 'FileReadyToParse' )
    )
  
    event_data = BuildRequest( filepath = filepath,
                               filetype = 'cpp',
                               compilation_flags = [ '-x', 'c++' ],
                               line_num = 81,
                               column_num = 17,
                               contents = contents,
                               command_arguments = [ 'GetDocImprecise' ] )
  
    response = app.post_json( '/run_completer_command',
                              event_data,
                              expect_errors = True )
  
    assert_that( response.status_code,
                 equal_to( requests.codes.internal_server_error ) )
  
    assert_that( response.json,
                 ErrorMatcher( ValueError, NO_DOCUMENTATION_MESSAGE ) )
  
  
  @SharedYcmd
  def Subcommands_GetDocImprecise_NoCursor_test( app ):
    filepath = PathToTestFile( 'GetDoc_Clang.cc' )
    contents = ReadFile( filepath )
  
    app.post_json(
      '/event_notification',
      BuildRequest( filepath = filepath,
                    filetype = 'cpp',
                    compilation_flags = [ '-x', 'c++' ],
                    contents = contents,
                    event_name = 'FileReadyToParse' )
    )
  
    event_data = BuildRequest( filepath = filepath,
                               filetype = 'cpp',
                               compilation_flags = [ '-x', 'c++' ],
                               line_num = 1,
                               column_num = 1,
                               contents = contents,
                               command_arguments = [ 'GetDocImprecise' ] )
  
    response = app.post_json( '/run_completer_command',
                              event_data,
                              expect_errors = True )
  
    assert_that( response.status_code,
                 equal_to( requests.codes.internal_server_error ) )
  
    assert_that( response.json,
                 ErrorMatcher( ValueError, NO_DOCUMENTATION_MESSAGE ) )
  
  
  @SharedYcmd
  def Subcommands_GetDocImprecise_NoReadyToParse_test( app ):
    filepath = PathToTestFile( 'GetDoc_Clang.cc' )
    contents = ReadFile( filepath )
  
    event_data = BuildRequest( filepath = filepath,
                               filetype = 'cpp',
                               compilation_flags = [ '-x', 'c++' ],
                               line_num = 11,
                               column_num = 18,
                               contents = contents,
                               command_arguments = [ 'GetDocImprecise' ] )
  
    response = app.post_json( '/run_completer_command', event_data ).json
  
    assert_that( response, has_entry(
      'detailed_info', """\
  int get_a_global_variable(bool test)
  This is a method which is only pretend global
  Type: int (bool)
  Name: get_a_global_variable
  ---
  This is a method which is only pretend global
  @param test Set this to true. Do it.""" ) )
  
  
  @SharedYcmd
  def Subcommands_GetDocImprecise_SystemHeaders_test( app ):
    app.post_json( '/load_extra_conf_file', {
      'filepath': PathToTestFile( 'get_doc', '.ycm_extra_conf.py' ) } )
  
    filepath = PathToTestFile( 'get_doc', 'test.cpp' )
    contents = ReadFile( filepath )
  
    event_data = BuildRequest( filepath = filepath,
                               filetype = 'cpp',
                               line_num = 4,
                               column_num = 7,
                               contents = contents,
                               command_arguments = [ 'GetDocImprecise' ] )
  
    response = app.post_json( '/run_completer_command', event_data ).json
  
    assert_that( response,
                 has_entry( 'detailed_info', """\
  int test()
  This is a function.
  Type: int ()
  Name: test
  ---
  
  \\brief This is a function.
  
  This function is defined in a system header.
  """ ) )
  
  
  @SharedYcmd
  def Subcommands_GetDoc_Unicode_test( app ):
    filepath = PathToTestFile( 'unicode.cc' )
    contents = ReadFile( filepath )
  
    event_data = BuildRequest( filepath = filepath,
                               filetype = 'cpp',
                               compilation_flags = [ '-x', 'c++' ],
                               line_num = 21,
                               column_num = 16,
                               contents = contents,
                               command_arguments = [ 'GetDoc' ],
                               completer_target = 'filetype_default' )
  
    response = app.post_json( '/run_completer_command', event_data ).json
  
    pprint( response )
  
    assert_that( response, has_entry(
      'detailed_info', """\
  int member_with_å_unicøde
  This method has unicøde in it
  Type: int
  Name: member_with_å_unicøde
  ---
  
  This method has unicøde in it
  """ ) )
  
  
  @SharedYcmd
  def Subcommands_GetDoc_CUDA_test( app ):
    filepath = PathToTestFile( 'cuda', 'basic.cu' )
    contents = ReadFile( filepath )
  
    event_data = BuildRequest( filepath = filepath,
                               filetype = 'cuda',
                               compilation_flags = [ '-x', 'cuda' ],
                               line_num = 8,
                               column_num = 3,
                               contents = contents,
                               command_arguments = [ 'GetDoc' ],
                               completer_target = 'filetype_default' )
  
    response = app.post_json( '/run_completer_command', event_data ).json
  
    pprint( response )
  
    assert_that( response, has_entry(
      'detailed_info', """\
  void kernel()
  This is a test kernel
  Type: void ()
  Name: kernel
  ---
  This is a test kernel""" ) )
  
  
  def Subcommands_StillParsingError( app, command ):
    filepath = PathToTestFile( 'test.cpp' )
  
    data = BuildRequest( command_arguments = [ command ],
                         compilation_flags = [ '-x', 'c++' ],
                         line_num = 1,
                         column_num = 1,
                         filepath = filepath,
                         contents = '',
                         filetype = 'cpp' )
  
    response = app.post_json( '/run_completer_command',
                              data,
                              expect_errors = True )
  
    assert_that( response.status_code,
                 equal_to( requests.codes.internal_server_error ) )
  
    pprint( response.json )
  
    assert_that( response.json, ErrorMatcher( RuntimeError,
                                              PARSING_FILE_MESSAGE ) )
  
  
  @SharedYcmd
  def Subcommands_StillParsingError_test( app ):
    completer = handlers._server_state.GetFiletypeCompleter( [ 'cpp' ] )
    with patch.object( completer, '_completer', MockCoreClangCompleter() ):
      Subcommands_StillParsingError( app, 'FixIt' )
      Subcommands_StillParsingError( app, 'GetDoc' )
      Subcommands_StillParsingError( app, 'GetDocImprecise' )
      Subcommands_StillParsingError( app, 'GetParent' )
      Subcommands_StillParsingError( app, 'GetType' )
      Subcommands_StillParsingError( app, 'GetTypeImprecise' )
      Subcommands_StillParsingError( app, 'GoTo' )
      Subcommands_StillParsingError( app, 'GoToDeclaration' )
      Subcommands_StillParsingError( app, 'GoToDefinition' )
      Subcommands_StillParsingError( app, 'GoToImprecise' )
  