diff --git a/python/ycm/tests/event_notification_test.py b/python/ycm/tests/event_notification_test.py index 9ce63b94..716703bc 100644 --- a/python/ycm/tests/event_notification_test.py +++ b/python/ycm/tests/event_notification_test.py @@ -361,7 +361,7 @@ def EventNotification_FileReadyToParse_TagFiles_UnicodeWorkingDirectory_test( with patch( 'ycm.client.event_notification.EventNotification.' 'PostDataToHandlerAsync' ) as post_data_to_handler_async: with CurrentWorkingDirectory( unicode_dir ): - with MockVimBuffers( [ current_buffer ], current_buffer, ( 6, 5 ) ): + with MockVimBuffers( [ current_buffer ], current_buffer, ( 1, 5 ) ): ycm.OnFileReadyToParse() assert_that( @@ -370,7 +370,7 @@ def EventNotification_FileReadyToParse_TagFiles_UnicodeWorkingDirectory_test( contains( has_entries( { 'filepath': current_buffer_file, - 'line_num': 6, + 'line_num': 1, 'column_num': 6, 'file_data': has_entries( { current_buffer_file: has_entries( { @@ -416,7 +416,7 @@ def EventNotification_BufferVisit_BuildRequestForCurrentAndUnsavedBuffers_test( 'PostDataToHandlerAsync' ) as post_data_to_handler_async: with MockVimBuffers( [ current_buffer, modified_buffer, unmodified_buffer ], current_buffer, - ( 3, 5 ) ): + ( 1, 5 ) ): ycm.OnBufferVisit() assert_that( @@ -425,7 +425,7 @@ def EventNotification_BufferVisit_BuildRequestForCurrentAndUnsavedBuffers_test( contains( has_entries( { 'filepath': current_buffer_file, - 'line_num': 3, + 'line_num': 1, 'column_num': 6, 'file_data': has_entries( { current_buffer_file: has_entries( { diff --git a/python/ycm/tests/omni_completer_test.py b/python/ycm/tests/omni_completer_test.py index a5a51ebd..a892813a 100644 --- a/python/ycm/tests/omni_completer_test.py +++ b/python/ycm/tests/omni_completer_test.py @@ -24,690 +24,600 @@ from __future__ import absolute_import # Not installing aliases from python-future; it's unreliable and slow. from builtins import * # noqa -from future.utils import PY2 -from mock import patch, call -from nose.tools import eq_ +from hamcrest import assert_that, contains, contains_string, empty, has_entries -from ycm.tests.test_utils import ExpectedFailure, ExtendedMock, MockVimModule +from ycm.tests.test_utils import ( ExpectedFailure, MockVimBuffers, + MockVimModule, ToBytesOnPY2, VimBuffer ) MockVimModule() from ycm.tests import YouCompleteMeInstance -from ycmd.utils import ToBytes -from ycmd.request_wrap import RequestWrap - - -def ToBytesOnPY2( data ): - # To test the omnifunc, etc. returning strings, which can be of different - # types depending on python version, we use ToBytes on PY2 and just the native - # str on python3. This roughly matches what happens between py2 and py3 - # versions within Vim - if PY2: - return ToBytes( data ) - - return data - - -def BuildRequest( line_num, column_num, contents ): - # Note: it would be nice to use ycmd.tests.test_utils.BuildRequest directly - # here, but we can't import ycmd.tests.test_utils because that in turn imports - # ycm_core, which would cause our "ycm_core not imported" test to fail. - return { - 'line_num': line_num, - 'column_num': column_num, - 'filepath': '/test', - 'file_data': { - '/test': { - 'contents': contents, - 'filetypes': [ 'java' ] # We need a filetype with a trigger, so we just - # use java - } - } - } - - -def BuildRequestWrap( line_num, column_num, contents ): - return RequestWrap( BuildRequest( line_num, column_num, contents ) ) - @YouCompleteMeInstance( { 'cache_omnifunc': 1 } ) def OmniCompleter_GetCompletions_Cache_List_test( ycm ): - contents = 'test.' - request_data = BuildRequestWrap( line_num = 1, - column_num = 6, - contents = contents ) + def Omnifunc( findstart, base ): + if findstart: + return 5 + return [ 'a', 'b', 'cdef' ] - # Make sure there is an omnifunc set up. - with patch( 'vim.eval', return_value = ToBytesOnPY2( 'test_omnifunc' ) ): - ycm._omnicomp.OnFileReadyToParse( request_data ) + current_buffer = VimBuffer( 'buffer', + contents = [ 'test.' ], + filetype = 'java', + omnifunc = Omnifunc ) - omnifunc_result = [ ToBytesOnPY2( 'a' ), - ToBytesOnPY2( 'b' ), - ToBytesOnPY2( 'cdef' ) ] - - # And get the completions - with patch( 'vim.eval', - new_callable = ExtendedMock, - side_effect = [ 6, omnifunc_result ] ) as vim_eval: - - results = ycm._omnicomp.ComputeCandidates( request_data ) - vim_eval.assert_has_exact_calls( [ - call( 'test_omnifunc(1,"")' ), - call( "test_omnifunc(0,'')" ), - ] ) - - eq_( results, omnifunc_result ) + with MockVimBuffers( [ current_buffer ], current_buffer, ( 1, 5 ) ): + # Make sure there is an omnifunc set up. + ycm.OnFileReadyToParse() + ycm.SendCompletionRequest() + assert_that( + ycm.GetCompletionResponse(), + has_entries( { + 'completions': ToBytesOnPY2( [ 'a', 'b', 'cdef' ] ), + 'completion_start_column': 6 + } ) + ) @YouCompleteMeInstance( { 'cache_omnifunc': 1 } ) def OmniCompleter_GetCompletions_Cache_ListFilter_test( ycm ): - contents = 'test.t' - request_data = BuildRequestWrap( line_num = 1, - column_num = 7, - contents = contents ) + def Omnifunc( findstart, base ): + if findstart: + return 5 + return [ 'a', 'b', 'cdef' ] - eq_( request_data[ 'query' ], 't' ) + current_buffer = VimBuffer( 'buffer', + contents = [ 'test.t' ], + filetype = 'java', + omnifunc = Omnifunc ) - # Make sure there is an omnifunc set up. - with patch( 'vim.eval', return_value = ToBytesOnPY2( 'test_omnifunc' ) ): - ycm._omnicomp.OnFileReadyToParse( request_data ) - - omnifunc_result = [ ToBytesOnPY2( 'a' ), - ToBytesOnPY2( 'b' ), - ToBytesOnPY2( 'cdef' ) ] - - # And get the completions - with patch( 'vim.eval', - new_callable = ExtendedMock, - side_effect = [ 5, omnifunc_result ] ) as vim_eval: - - results = ycm._omnicomp.ComputeCandidates( request_data ) - vim_eval.assert_has_exact_calls( [ - call( 'test_omnifunc(1,"")' ), - call( "test_omnifunc(0,'t')" ), - ] ) - - eq_( results, [] ) + with MockVimBuffers( [ current_buffer ], current_buffer, ( 1, 6 ) ): + # Make sure there is an omnifunc set up. + ycm.OnFileReadyToParse() + ycm.SendCompletionRequest() + assert_that( + ycm.GetCompletionResponse(), + has_entries( { + 'completions': empty(), + 'completion_start_column': 6 + } ) + ) @YouCompleteMeInstance( { 'cache_omnifunc': 0 } ) def OmniCompleter_GetCompletions_NoCache_List_test( ycm ): - contents = 'test.' - request_data = BuildRequestWrap( line_num = 1, - column_num = 6, - contents = contents ) + def Omnifunc( findstart, base ): + if findstart: + return 5 + return [ 'a', 'b', 'cdef' ] + current_buffer = VimBuffer( 'buffer', + contents = [ 'test.' ], + filetype = 'java', + omnifunc = Omnifunc ) - # Make sure there is an omnifunc set up. - with patch( 'vim.eval', return_value = ToBytesOnPY2( 'test_omnifunc' ) ): - ycm._omnicomp.OnFileReadyToParse( request_data ) - - omnifunc_result = [ ToBytesOnPY2( 'a' ), - ToBytesOnPY2( 'b' ), - ToBytesOnPY2( 'cdef' ) ] - - # And get the completions - with patch( 'vim.eval', - new_callable = ExtendedMock, - side_effect = [ 5, omnifunc_result ] ) as vim_eval: - - results = ycm._omnicomp.ComputeCandidates( request_data ) - vim_eval.assert_has_exact_calls( [ - call( 'test_omnifunc(1,"")' ), - call( "test_omnifunc(0,'')" ), - ] ) - - eq_( results, omnifunc_result ) + with MockVimBuffers( [ current_buffer ], current_buffer, ( 1, 5 ) ): + # Make sure there is an omnifunc set up. + ycm.OnFileReadyToParse() + ycm.SendCompletionRequest() + assert_that( + ycm.GetCompletionResponse(), + has_entries( { + 'completions': ToBytesOnPY2( [ 'a', 'b', 'cdef' ] ), + 'completion_start_column': 6 + } ) + ) @YouCompleteMeInstance( { 'cache_omnifunc': 0 } ) def OmniCompleter_GetCompletions_NoCache_ListFilter_test( ycm ): - contents = 'test.t' - request_data = BuildRequestWrap( line_num = 1, - column_num = 7, - contents = contents ) + def Omnifunc( findstart, base ): + if findstart: + return 5 + return [ 'a', 'b', 'cdef' ] - eq_( request_data[ 'query' ], 't' ) + current_buffer = VimBuffer( 'buffer', + contents = [ 'test.t' ], + filetype = 'java', + omnifunc = Omnifunc ) - # Make sure there is an omnifunc set up. - with patch( 'vim.eval', return_value = ToBytesOnPY2( 'test_omnifunc' ) ): - ycm._omnicomp.OnFileReadyToParse( request_data ) - - omnifunc_result = [ ToBytesOnPY2( 'a' ), - ToBytesOnPY2( 'b' ), - ToBytesOnPY2( 'cdef' ) ] - - # And get the completions - with patch( 'vim.eval', - new_callable = ExtendedMock, - side_effect = [ 5, omnifunc_result ] ) as vim_eval: - - results = ycm._omnicomp.ComputeCandidates( request_data ) - vim_eval.assert_has_exact_calls( [ - call( 'test_omnifunc(1,"")' ), - call( "test_omnifunc(0,'t')" ), - ] ) - - # actual result is that the results are not filtered, as we expect the - # omniufunc or vim itself to do this filtering - eq_( results, omnifunc_result ) + with MockVimBuffers( [ current_buffer ], current_buffer, ( 1, 6 ) ): + # Make sure there is an omnifunc set up. + ycm.OnFileReadyToParse() + ycm.SendCompletionRequest() + # Actual result is that the results are not filtered, as we expect the + # omnifunc or vim itself to do this filtering. + assert_that( + ycm.GetCompletionResponse(), + has_entries( { + 'completions': ToBytesOnPY2( [ 'a', 'b', 'cdef' ] ), + 'completion_start_column': 6 + } ) + ) @YouCompleteMeInstance( { 'cache_omnifunc': 0 } ) -def OmniCompleter_GetCompletsions_NoCache_UseFindStart_test( ycm ): - contents = 'test.t' - request_data = BuildRequestWrap( line_num = 1, - column_num = 7, - contents = contents ) +def OmniCompleter_GetCompletions_NoCache_UseFindStart_test( ycm ): + def Omnifunc( findstart, base ): + if findstart: + return 0 + return [ 'a', 'b', 'cdef' ] - eq_( request_data[ 'query' ], 't' ) + current_buffer = VimBuffer( 'buffer', + contents = [ 'test.t' ], + filetype = 'java', + omnifunc = Omnifunc ) - # Make sure there is an omnifunc set up. - with patch( 'vim.eval', return_value = ToBytesOnPY2( 'test_omnifunc' ) ): - ycm._omnicomp.OnFileReadyToParse( request_data ) - - omnifunc_result = [ ToBytesOnPY2( 'a' ), - ToBytesOnPY2( 'b' ), - ToBytesOnPY2( 'cdef' ) ] - - # And get the completions - with patch( 'vim.eval', - new_callable = ExtendedMock, - side_effect = [ 0, omnifunc_result ] ) as vim_eval: - results = ycm._omnicomp.ComputeCandidates( request_data ) - - vim_eval.assert_has_exact_calls( [ - call( 'test_omnifunc(1,"")' ), - call( "test_omnifunc(0,'test.t')" ), - ] ) - - eq_( results, omnifunc_result ) + with MockVimBuffers( [ current_buffer ], current_buffer, ( 1, 6 ) ): + # Make sure there is an omnifunc set up. + ycm.OnFileReadyToParse() + ycm.SendCompletionRequest() + # Actual result is that the results are not filtered, as we expect the + # omnifunc or vim itself to do this filtering. + assert_that( + ycm.GetCompletionResponse(), + has_entries( { + 'completions': ToBytesOnPY2( [ 'a', 'b', 'cdef' ] ), + 'completion_start_column': 1 + } ) + ) @YouCompleteMeInstance( { 'cache_omnifunc': 1 } ) -def OmniCompleter_GetCompletsions_Cache_UseFindStart_test( ycm ): - contents = 'test.t' - request_data = BuildRequestWrap( line_num = 1, - column_num = 7, - contents = contents ) +def OmniCompleter_GetCompletions_Cache_UseFindStart_test( ycm ): + def Omnifunc( findstart, base ): + if findstart: + return 0 + return [ 'a', 'b', 'cdef' ] - eq_( request_data[ 'query' ], 't' ) - - # Make sure there is an omnifunc set up. - with patch( 'vim.eval', return_value = ToBytesOnPY2( 'test_omnifunc' ) ): - ycm._omnicomp.OnFileReadyToParse( request_data ) - - omnifunc_result = [ ToBytesOnPY2( 'a' ), - ToBytesOnPY2( 'b' ), - ToBytesOnPY2( 'cdef' ) ] - - # And get the completions - with patch( 'vim.eval', - new_callable = ExtendedMock, - side_effect = [ 0, omnifunc_result ] ) as vim_eval: - results = ycm._omnicomp.ComputeCandidates( request_data ) - - vim_eval.assert_has_exact_calls( [ - call( 'test_omnifunc(1,"")' ), - call( "test_omnifunc(0,'test.t')" ), - ] ) + current_buffer = VimBuffer( 'buffer', + contents = [ 'test.t' ], + filetype = 'java', + omnifunc = Omnifunc ) + with MockVimBuffers( [ current_buffer ], current_buffer, ( 1, 6 ) ): + # Make sure there is an omnifunc set up. + ycm.OnFileReadyToParse() + ycm.SendCompletionRequest() # There are no results because the query 'test.t' doesn't match any - # candidate (and cache_omnifunc=1, so we FilterAndSortCandidates) - eq_( results, [] ) + # candidate (and cache_omnifunc=1, so we FilterAndSortCandidates). + assert_that( + ycm.GetCompletionResponse(), + has_entries( { + 'completions': empty(), + 'completion_start_column': 1 + } ) + ) @YouCompleteMeInstance( { 'cache_omnifunc': 1 } ) def OmniCompleter_GetCompletions_Cache_Object_test( ycm ): - contents = 'test.t' - request_data = BuildRequestWrap( line_num = 1, - column_num = 7, - contents = contents ) + def Omnifunc( findstart, base ): + if findstart: + return 5 + return { 'words': [ 'a', 'b', 'CDtEF' ] } - eq_( request_data[ 'query' ], 't' ) + current_buffer = VimBuffer( 'buffer', + contents = [ 'test.t' ], + filetype = 'java', + omnifunc = Omnifunc ) - # Make sure there is an omnifunc set up. - with patch( 'vim.eval', return_value = ToBytesOnPY2( 'test_omnifunc' ) ): - ycm._omnicomp.OnFileReadyToParse( request_data ) - - omnifunc_result = { - 'words': [ - ToBytesOnPY2( 'a' ), - ToBytesOnPY2( 'b' ), - ToBytesOnPY2( 'CDtEF' ) - ] - } - - # And get the completions - with patch( 'vim.eval', - new_callable = ExtendedMock, - side_effect = [ 5, omnifunc_result ] ) as vim_eval: - - results = ycm._omnicomp.ComputeCandidates( request_data ) - - vim_eval.assert_has_exact_calls( [ - call( 'test_omnifunc(1,"")' ), - call( "test_omnifunc(0,'t')" ), - ] ) - - eq_( results, [ ToBytesOnPY2( 'CDtEF' ) ] ) + with MockVimBuffers( [ current_buffer ], current_buffer, ( 1, 6 ) ): + # Make sure there is an omnifunc set up. + ycm.OnFileReadyToParse() + ycm.SendCompletionRequest() + assert_that( + ycm.GetCompletionResponse(), + has_entries( { + 'completions': [ 'CDtEF' ], + 'completion_start_column': 6 + } ) + ) @YouCompleteMeInstance( { 'cache_omnifunc': 1 } ) def OmniCompleter_GetCompletions_Cache_ObjectList_test( ycm ): - contents = 'test.tt' - request_data = BuildRequestWrap( line_num = 1, - column_num = 8, - contents = contents ) + def Omnifunc( findstart, base ): + if findstart: + return 5 + return [ + { + 'word': 'a', + 'abbr': 'ABBR', + 'menu': 'MENU', + 'info': 'INFO', + 'kind': 'K' + }, + { + 'word': 'test', + 'abbr': 'ABBRTEST', + 'menu': 'MENUTEST', + 'info': 'INFOTEST', + 'kind': 'T' + } + ] - eq_( request_data[ 'query' ], 'tt' ) + current_buffer = VimBuffer( 'buffer', + contents = [ 'test.tt' ], + filetype = 'java', + omnifunc = Omnifunc ) - # Make sure there is an omnifunc set up. - with patch( 'vim.eval', return_value = ToBytesOnPY2( 'test_omnifunc' ) ): - ycm._omnicomp.OnFileReadyToParse( request_data ) - - omnifunc_result = [ - { - 'word': ToBytesOnPY2( 'a' ), - 'abbr': ToBytesOnPY2( 'ABBR'), - 'menu': ToBytesOnPY2( 'MENU' ), - 'info': ToBytesOnPY2( 'INFO' ), - 'kind': ToBytesOnPY2( 'K' ) - }, - { - 'word': ToBytesOnPY2( 'test' ), - 'abbr': ToBytesOnPY2( 'ABBRTEST'), - 'menu': ToBytesOnPY2( 'MENUTEST' ), - 'info': ToBytesOnPY2( 'INFOTEST' ), - 'kind': ToBytesOnPY2( 'T' ) - } - ] - - # And get the completions - with patch( 'vim.eval', - new_callable = ExtendedMock, - side_effect = [ 5, omnifunc_result ] ) as vim_eval: - - results = ycm._omnicomp.ComputeCandidates( request_data ) - - vim_eval.assert_has_exact_calls( [ - call( 'test_omnifunc(1,"")' ), - call( "test_omnifunc(0,'tt')" ), - ] ) - - eq_( results, [ omnifunc_result[ 1 ] ] ) + with MockVimBuffers( [ current_buffer ], current_buffer, ( 1, 7 ) ): + # Make sure there is an omnifunc set up. + ycm.OnFileReadyToParse() + ycm.SendCompletionRequest() + assert_that( + ycm.GetCompletionResponse(), + has_entries( { + 'completions': contains( { + 'word': 'test', + 'abbr': 'ABBRTEST', + 'menu': 'MENUTEST', + 'info': 'INFOTEST', + 'kind': 'T' + } ), + 'completion_start_column': 6 + } ) + ) @YouCompleteMeInstance( { 'cache_omnifunc': 0 } ) def OmniCompleter_GetCompletions_NoCache_ObjectList_test( ycm ): - contents = 'test.tt' - request_data = BuildRequestWrap( line_num = 1, - column_num = 8, - contents = contents ) + def Omnifunc( findstart, base ): + if findstart: + return 5 + return [ + { + 'word': 'a', + 'abbr': 'ABBR', + 'menu': 'MENU', + 'info': 'INFO', + 'kind': 'K' + }, + { + 'word': 'test', + 'abbr': 'ABBRTEST', + 'menu': 'MENUTEST', + 'info': 'INFOTEST', + 'kind': 'T' + } + ] - eq_( request_data[ 'query' ], 'tt' ) - - # Make sure there is an omnifunc set up. - with patch( 'vim.eval', return_value = ToBytesOnPY2( 'test_omnifunc' ) ): - ycm._omnicomp.OnFileReadyToParse( request_data ) - - omnifunc_result = [ - { - 'word': ToBytesOnPY2( 'a' ), - 'abbr': ToBytesOnPY2( 'ABBR'), - 'menu': ToBytesOnPY2( 'MENU' ), - 'info': ToBytesOnPY2( 'INFO' ), - 'kind': ToBytesOnPY2( 'K' ) - }, - { - 'word': ToBytesOnPY2( 'test' ), - 'abbr': ToBytesOnPY2( 'ABBRTEST'), - 'menu': ToBytesOnPY2( 'MENUTEST' ), - 'info': ToBytesOnPY2( 'INFOTEST' ), - 'kind': ToBytesOnPY2( 'T' ) - } - ] - - # And get the completions - with patch( 'vim.eval', - new_callable = ExtendedMock, - side_effect = [ 5, omnifunc_result ] ) as vim_eval: - - results = ycm._omnicomp.ComputeCandidates( request_data ) - - vim_eval.assert_has_exact_calls( [ - call( 'test_omnifunc(1,"")' ), - call( "test_omnifunc(0,'tt')" ), - ] ) + current_buffer = VimBuffer( 'buffer', + contents = [ 'test.tt' ], + filetype = 'java', + omnifunc = Omnifunc ) + with MockVimBuffers( [ current_buffer ], current_buffer, ( 1, 7 ) ): + # Make sure there is an omnifunc set up. + ycm.OnFileReadyToParse() + ycm.SendCompletionRequest() # We don't filter the result - we expect the omnifunc to do that - # based on the query we supplied (Note: that means no fuzzy matching!) - eq_( results, omnifunc_result ) + # based on the query we supplied (Note: that means no fuzzy matching!). + assert_that( + ycm.GetCompletionResponse(), + has_entries( { + 'completions': ToBytesOnPY2( [ { + 'word': 'a', + 'abbr': 'ABBR', + 'menu': 'MENU', + 'info': 'INFO', + 'kind': 'K' + }, { + 'word': 'test', + 'abbr': 'ABBRTEST', + 'menu': 'MENUTEST', + 'info': 'INFOTEST', + 'kind': 'T' + } ] ), + 'completion_start_column': 6 + } ) + ) @YouCompleteMeInstance( { 'cache_omnifunc': 1 } ) def OmniCompleter_GetCompletions_Cache_ObjectListObject_test( ycm ): - contents = 'test.tt' - request_data = BuildRequestWrap( line_num = 1, - column_num = 8, - contents = contents ) - - eq_( request_data[ 'query' ], 'tt' ) - - # Make sure there is an omnifunc set up. - with patch( 'vim.eval', return_value = ToBytesOnPY2( 'test_omnifunc' ) ): - ycm._omnicomp.OnFileReadyToParse( request_data ) - - omnifunc_result = { - 'words': [ + def Omnifunc( findstart, base ): + if findstart: + return 5 + return { 'words': [ { - 'word': ToBytesOnPY2( 'a' ), - 'abbr': ToBytesOnPY2( 'ABBR'), - 'menu': ToBytesOnPY2( 'MENU' ), - 'info': ToBytesOnPY2( 'INFO' ), - 'kind': ToBytesOnPY2( 'K' ) + 'word': 'a', + 'abbr': 'ABBR', + 'menu': 'MENU', + 'info': 'INFO', + 'kind': 'K' }, { - 'word': ToBytesOnPY2( 'test' ), - 'abbr': ToBytesOnPY2( 'ABBRTEST'), - 'menu': ToBytesOnPY2( 'MENUTEST' ), - 'info': ToBytesOnPY2( 'INFOTEST' ), - 'kind': ToBytesOnPY2( 'T' ) + 'word': 'test', + 'abbr': 'ABBRTEST', + 'menu': 'MENUTEST', + 'info': 'INFOTEST', + 'kind': 'T' } - ] - } + ] } - # And get the completions - with patch( 'vim.eval', - new_callable = ExtendedMock, - side_effect = [ 5, omnifunc_result ] ) as vim_eval: + current_buffer = VimBuffer( 'buffer', + contents = [ 'test.tt' ], + filetype = 'java', + omnifunc = Omnifunc ) - results = ycm._omnicomp.ComputeCandidates( request_data ) - - vim_eval.assert_has_exact_calls( [ - call( 'test_omnifunc(1,"")' ), - call( "test_omnifunc(0,'tt')" ), - ] ) - - eq_( results, [ omnifunc_result[ 'words' ][ 1 ] ] ) + with MockVimBuffers( [ current_buffer ], current_buffer, ( 1, 7 ) ): + # Make sure there is an omnifunc set up. + ycm.OnFileReadyToParse() + ycm.SendCompletionRequest() + assert_that( + ycm.GetCompletionResponse(), + has_entries( { + 'completions': ToBytesOnPY2( [ { + 'word': 'test', + 'abbr': 'ABBRTEST', + 'menu': 'MENUTEST', + 'info': 'INFOTEST', + 'kind': 'T' + } ] ), + 'completion_start_column': 6 + } ) + ) @YouCompleteMeInstance( { 'cache_omnifunc': 0 } ) def OmniCompleter_GetCompletions_NoCache_ObjectListObject_test( ycm ): - contents = 'test.tt' - request_data = BuildRequestWrap( line_num = 1, - column_num = 8, - contents = contents ) - - eq_( request_data[ 'query' ], 'tt' ) - - # Make sure there is an omnifunc set up. - with patch( 'vim.eval', return_value = ToBytesOnPY2( 'test_omnifunc' ) ): - ycm._omnicomp.OnFileReadyToParse( request_data ) - - omnifunc_result = { - 'words': [ + def Omnifunc( findstart, base ): + if findstart: + return 5 + return { 'words': [ { - 'word': ToBytesOnPY2( 'a' ), - 'abbr': ToBytesOnPY2( 'ABBR'), - 'menu': ToBytesOnPY2( 'MENU' ), - 'info': ToBytesOnPY2( 'INFO' ), - 'kind': ToBytesOnPY2( 'K' ) + 'word': 'a', + 'abbr': 'ABBR', + 'menu': 'MENU', + 'info': 'INFO', + 'kind': 'K' }, { - 'word': ToBytesOnPY2( 'test' ), - 'abbr': ToBytesOnPY2( 'ABBRTEST'), - 'menu': ToBytesOnPY2( 'MENUTEST' ), - 'info': ToBytesOnPY2( 'INFOTEST' ), - 'kind': ToBytesOnPY2( 'T' ) + 'word': 'test', + 'abbr': 'ABBRTEST', + 'menu': 'MENUTEST', + 'info': 'INFOTEST', + 'kind': 'T' } - ] - } + ] } - # And get the completions - with patch( 'vim.eval', - new_callable = ExtendedMock, - side_effect = [ 5, omnifunc_result ] ) as vim_eval: - - results = ycm._omnicomp.ComputeCandidates( request_data ) - - vim_eval.assert_has_exact_calls( [ - call( 'test_omnifunc(1,"")' ), - call( "test_omnifunc(0,'tt')" ), - ] ) + current_buffer = VimBuffer( 'buffer', + contents = [ 'test.tt' ], + filetype = 'java', + omnifunc = Omnifunc ) + with MockVimBuffers( [ current_buffer ], current_buffer, ( 1, 7 ) ): + # Make sure there is an omnifunc set up. + ycm.OnFileReadyToParse() + ycm.SendCompletionRequest() # No FilterAndSortCandidates for cache_omnifunc=0 (we expect the omnifunc # to do the filtering?) - eq_( results, omnifunc_result[ 'words' ] ) + assert_that( + ycm.GetCompletionResponse(), + has_entries( { + 'completions': ToBytesOnPY2( [ { + 'word': 'a', + 'abbr': 'ABBR', + 'menu': 'MENU', + 'info': 'INFO', + 'kind': 'K' + }, { + 'word': 'test', + 'abbr': 'ABBRTEST', + 'menu': 'MENUTEST', + 'info': 'INFOTEST', + 'kind': 'T' + } ] ), + 'completion_start_column': 6 + } ) + ) @YouCompleteMeInstance( { 'cache_omnifunc': 1 } ) def OmniCompleter_GetCompletions_Cache_List_Unicode_test( ycm ): - contents = '†åsty_π.' - request_data = BuildRequestWrap( line_num = 1, - column_num = 13, - contents = contents ) + def Omnifunc( findstart, base ): + if findstart: + return 12 + return [ '†est', 'å_unicode_identifier', 'πππππππ yummy πie' ] + + current_buffer = VimBuffer( 'buffer', + contents = [ '†åsty_π.' ], + filetype = 'java', + omnifunc = Omnifunc ) + + with MockVimBuffers( [ current_buffer ], current_buffer, ( 1, 12 ) ): + # Make sure there is an omnifunc set up. + ycm.OnFileReadyToParse() + ycm.SendCompletionRequest() + assert_that( + ycm.GetCompletionResponse(), + has_entries( { + 'completions': ToBytesOnPY2( [ '†est', + 'å_unicode_identifier', + 'πππππππ yummy πie' ] ), + 'completion_start_column': 13 + } ) + ) - # Make sure there is an omnifunc set up. - with patch( 'vim.eval', return_value = ToBytesOnPY2( 'test_omnifunc' ) ): - ycm._omnicomp.OnFileReadyToParse( request_data ) - - omnifunc_result = [ ToBytesOnPY2( '†est' ), - ToBytesOnPY2( 'å_unicode_identifier' ), - ToBytesOnPY2( 'πππππππ yummy πie' ) ] - - # And get the completions - with patch( 'vim.eval', - new_callable = ExtendedMock, - side_effect = [ 12, omnifunc_result ] ) as vim_eval: - - results = ycm._omnicomp.ComputeCandidates( request_data ) - vim_eval.assert_has_exact_calls( [ - call( 'test_omnifunc(1,"")' ), - call( "test_omnifunc(0,'')" ), - ] ) - - eq_( results, omnifunc_result ) - - -@YouCompleteMeInstance( { 'cache_omnifunc': 1 } ) +@YouCompleteMeInstance( { 'cache_omnifunc': 0 } ) def OmniCompleter_GetCompletions_NoCache_List_Unicode_test( ycm ): - contents = '†åsty_π.' - request_data = BuildRequestWrap( line_num = 1, - column_num = 13, - contents = contents ) + def Omnifunc( findstart, base ): + if findstart: + return 12 + return [ '†est', 'å_unicode_identifier', 'πππππππ yummy πie' ] + + current_buffer = VimBuffer( 'buffer', + contents = [ '†åsty_π.' ], + filetype = 'java', + omnifunc = Omnifunc ) + + with MockVimBuffers( [ current_buffer ], current_buffer, ( 1, 12 ) ): + # Make sure there is an omnifunc set up. + ycm.OnFileReadyToParse() + ycm.SendCompletionRequest() + assert_that( + ycm.GetCompletionResponse(), + has_entries( { + 'completions': ToBytesOnPY2( [ '†est', + 'å_unicode_identifier', + 'πππππππ yummy πie' ] ), + 'completion_start_column': 13 + } ) + ) - # Make sure there is an omnifunc set up. - with patch( 'vim.eval', return_value = ToBytesOnPY2( 'test_omnifunc' ) ): - ycm._omnicomp.OnFileReadyToParse( request_data ) - - omnifunc_result = [ ToBytesOnPY2( '†est' ), - ToBytesOnPY2( 'å_unicode_identifier' ), - ToBytesOnPY2( 'πππππππ yummy πie' ) ] - - # And get the completions - with patch( 'vim.eval', - new_callable = ExtendedMock, - side_effect = [ 12, omnifunc_result ] ) as vim_eval: - - results = ycm._omnicomp.ComputeCandidates( request_data ) - vim_eval.assert_has_exact_calls( [ - call( 'test_omnifunc(1,"")' ), - call( "test_omnifunc(0,'')" ), - ] ) - - eq_( results, omnifunc_result ) - - -@ExpectedFailure( 'Filtering on unicode is not supported by the server' ) +@ExpectedFailure( 'Filtering on unicode is not supported by the server', + contains_string( "value for 'completions' was <[]>" ) ) @YouCompleteMeInstance( { 'cache_omnifunc': 1 } ) def OmniCompleter_GetCompletions_Cache_List_Filter_Unicode_test( ycm ): - contents = '†åsty_π.ππ' - request_data = BuildRequestWrap( line_num = 1, - column_num = 17, - contents = contents ) + def Omnifunc( findstart, base ): + if findstart: + return 12 + return [ '†est', 'å_unicode_identifier', 'πππππππ yummy πie' ] + current_buffer = VimBuffer( 'buffer', + contents = [ '†åsty_π.ππ' ], + filetype = 'java', + omnifunc = Omnifunc ) - # Make sure there is an omnifunc set up. - with patch( 'vim.eval', return_value = ToBytesOnPY2( 'test_omnifunc' ) ): - ycm._omnicomp.OnFileReadyToParse( request_data ) - - omnifunc_result = [ ToBytesOnPY2( '†est' ), - ToBytesOnPY2( 'å_unicode_identifier' ), - ToBytesOnPY2( 'πππππππ yummy πie' ) ] - - # And get the completions - with patch( 'vim.eval', - new_callable = ExtendedMock, - side_effect = [ 12, omnifunc_result ] ) as vim_eval: - - results = ycm._omnicomp.ComputeCandidates( request_data ) - vim_eval.assert_has_exact_calls( [ - call( 'test_omnifunc(1,"")' ), - call( "test_omnifunc(0,'ππ')" ), - ] ) - - # Fails here: Filtering on unicode is not supported - eq_( results, [ omnifunc_result[ 2 ] ] ) + with MockVimBuffers( [ current_buffer ], current_buffer, ( 1, 17 ) ): + # Make sure there is an omnifunc set up. + ycm.OnFileReadyToParse() + ycm.SendCompletionRequest() + assert_that( + ycm.GetCompletionResponse(), + has_entries( { + 'completions': ToBytesOnPY2( [ 'πππππππ yummy πie' ] ), + 'completion_start_column': 13 + } ) + ) @YouCompleteMeInstance( { 'cache_omnifunc': 0 } ) def OmniCompleter_GetCompletions_NoCache_List_Filter_Unicode_test( ycm ): - contents = '†åsty_π.ππ' - request_data = BuildRequestWrap( line_num = 1, - column_num = 17, - contents = contents ) + def Omnifunc( findstart, base ): + if findstart: + return 12 + return [ 'πππππππ yummy πie' ] + + current_buffer = VimBuffer( 'buffer', + contents = [ '†åsty_π.ππ' ], + filetype = 'java', + omnifunc = Omnifunc ) + + with MockVimBuffers( [ current_buffer ], current_buffer, ( 1, 17 ) ): + # Make sure there is an omnifunc set up. + ycm.OnFileReadyToParse() + ycm.SendCompletionRequest() + assert_that( + ycm.GetCompletionResponse(), + has_entries( { + 'completions': ToBytesOnPY2( [ 'πππππππ yummy πie' ] ), + 'completion_start_column': 13 + } ) + ) - # Make sure there is an omnifunc set up. - with patch( 'vim.eval', return_value = ToBytesOnPY2( 'test_omnifunc' ) ): - ycm._omnicomp.OnFileReadyToParse( request_data ) - - omnifunc_result = [ ToBytesOnPY2( 'πππππππ yummy πie' ) ] - - # And get the completions - with patch( 'vim.eval', - new_callable = ExtendedMock, - side_effect = [ 12, omnifunc_result ] ) as vim_eval: - - results = ycm._omnicomp.ComputeCandidates( request_data ) - vim_eval.assert_has_exact_calls( [ - call( 'test_omnifunc(1,"")' ), - call( "test_omnifunc(0,'ππ')" ), - ] ) - - eq_( results, omnifunc_result ) - - -@ExpectedFailure( 'Filtering on unicode is not supported by the server' ) +@ExpectedFailure( 'Filtering on unicode is not supported by the server', + contains_string( "value for 'completions' was <[]>" ) ) @YouCompleteMeInstance( { 'cache_omnifunc': 1 } ) def OmniCompleter_GetCompletions_Cache_ObjectList_Unicode_test( ycm ): - contents = '†åsty_π.ππ' - request_data = BuildRequestWrap( line_num = 1, - column_num = 17, - contents = contents ) + def Omnifunc( findstart, base ): + if findstart: + return 12 + return [ + { + 'word': 'ålpha∫et', + 'abbr': 'å∫∫®', + 'menu': 'µ´~¨á', + 'info': '^~fo', + 'kind': '˚' + }, + { + 'word': 'π†´ß†π', + 'abbr': 'ÅııÂʉÍÊ', + 'menu': '˜‰ˆËʉÍÊ', + 'info': 'ȈÏØʉÍÊ', + 'kind': 'Ê' + } + ] + current_buffer = VimBuffer( 'buffer', + contents = [ '†åsty_π.ππ' ], + filetype = 'java', + omnifunc = Omnifunc ) - eq_( request_data[ 'query' ], 'ππ' ) - - # Make sure there is an omnifunc set up. - with patch( 'vim.eval', return_value = ToBytesOnPY2( 'test_omnifunc' ) ): - ycm._omnicomp.OnFileReadyToParse( request_data ) - - omnifunc_result = [ - { - 'word': ToBytesOnPY2( 'ålpha∫et' ), - 'abbr': ToBytesOnPY2( 'å∫∫®'), - 'menu': ToBytesOnPY2( 'µ´~¨á' ), - 'info': ToBytesOnPY2( '^~fo' ), - 'kind': ToBytesOnPY2( '˚' ) - }, - { - 'word': ToBytesOnPY2( 'π†´ß†π' ), - 'abbr': ToBytesOnPY2( 'ÅııÂʉÍÊ'), - 'menu': ToBytesOnPY2( '˜‰ˆËʉÍÊ' ), - 'info': ToBytesOnPY2( 'ȈÏØʉÍÊ' ), - 'kind': ToBytesOnPY2( 'Ê' ) - } - ] - - # And get the completions - with patch( 'vim.eval', - new_callable = ExtendedMock, - side_effect = [ 12, omnifunc_result ] ) as vim_eval: - - results = ycm._omnicomp.ComputeCandidates( request_data ) - - vim_eval.assert_has_exact_calls( [ - call( 'test_omnifunc(1,"")' ), - call( "test_omnifunc(0,'ππ')" ), - ] ) - - # Fails here: Filtering on unicode is not supported - eq_( results, [ omnifunc_result[ 1 ] ] ) + with MockVimBuffers( [ current_buffer ], current_buffer, ( 1, 17 ) ): + # Make sure there is an omnifunc set up. + ycm.OnFileReadyToParse() + ycm.SendCompletionRequest() + assert_that( + ycm.GetCompletionResponse(), + has_entries( { + 'completions': ToBytesOnPY2( [ { + 'word': 'π†´ß†π', + 'abbr': 'ÅııÂʉÍÊ', + 'menu': '˜‰ˆËʉÍÊ', + 'info': 'ȈÏØʉÍÊ', + 'kind': 'Ê' + } ] ), + 'completion_start_column': 13 + } ) + ) @YouCompleteMeInstance( { 'cache_omnifunc': 1 } ) def OmniCompleter_GetCompletions_Cache_ObjectListObject_Unicode_test( ycm ): - contents = '†åsty_π.t' - request_data = BuildRequestWrap( line_num = 1, - column_num = 14, - contents = contents ) + def Omnifunc( findstart, base ): + if findstart: + return 12 + return { + 'words': [ + { + 'word': 'ålpha∫et', + 'abbr': 'å∫∫®', + 'menu': 'µ´~¨á', + 'info': '^~fo', + 'kind': '˚' + }, + { + 'word': 'π†´ß†π', + 'abbr': 'ÅııÂʉÍÊ', + 'menu': '˜‰ˆËʉÍÊ', + 'info': 'ȈÏØʉÍÊ', + 'kind': 'Ê' + }, + { + 'word': 'test', + 'abbr': 'ÅııÂʉÍÊ', + 'menu': '˜‰ˆËʉÍÊ', + 'info': 'ȈÏØʉÍÊ', + 'kind': 'Ê' + } + ] + } + current_buffer = VimBuffer( 'buffer', + contents = [ '†åsty_π.t' ], + filetype = 'java', + omnifunc = Omnifunc ) - eq_( request_data[ 'query' ], 't' ) - - # Make sure there is an omnifunc set up. - with patch( 'vim.eval', return_value = ToBytesOnPY2( 'test_omnifunc' ) ): - ycm._omnicomp.OnFileReadyToParse( request_data ) - - omnifunc_result = { - 'words': [ - { - 'word': ToBytesOnPY2( 'ålpha∫et' ), - 'abbr': ToBytesOnPY2( 'å∫∫®'), - 'menu': ToBytesOnPY2( 'µ´~¨á' ), - 'info': ToBytesOnPY2( '^~fo' ), - 'kind': ToBytesOnPY2( '˚' ) - }, - { - 'word': ToBytesOnPY2( 'π†´ß†π' ), - 'abbr': ToBytesOnPY2( 'ÅııÂʉÍÊ'), - 'menu': ToBytesOnPY2( '˜‰ˆËʉÍÊ' ), - 'info': ToBytesOnPY2( 'ȈÏØʉÍÊ' ), - 'kind': ToBytesOnPY2( 'Ê' ) - }, - { - 'word': ToBytesOnPY2( 'test' ), - 'abbr': ToBytesOnPY2( 'ÅııÂʉÍÊ'), - 'menu': ToBytesOnPY2( '˜‰ˆËʉÍÊ' ), - 'info': ToBytesOnPY2( 'ȈÏØʉÍÊ' ), - 'kind': ToBytesOnPY2( 'Ê' ) - } - ] - } - - # And get the completions - with patch( 'vim.eval', - new_callable = ExtendedMock, - side_effect = [ 12, omnifunc_result ] ) as vim_eval: - - results = ycm._omnicomp.ComputeCandidates( request_data ) - - vim_eval.assert_has_exact_calls( [ - call( 'test_omnifunc(1,"")' ), - call( "test_omnifunc(0,'t')" ), - ] ) - - # Note: the filtered results are all unicode objects (not bytes) because - # they are passed through the FilterAndSortCandidates machinery - # (via the server) - eq_( results, [ { - 'word': 'test', - 'abbr': 'ÅııÂʉÍÊ', - 'menu': '˜‰ˆËʉÍÊ', - 'info': 'ȈÏØʉÍÊ', - 'kind': 'Ê' - } ] ) + with MockVimBuffers( [ current_buffer ], current_buffer, ( 1, 13 ) ): + # Make sure there is an omnifunc set up. + ycm.OnFileReadyToParse() + ycm.SendCompletionRequest() + assert_that( + ycm.GetCompletionResponse(), + has_entries( { + 'completions': contains( { + 'word': 'test', + 'abbr': 'ÅııÂʉÍÊ', + 'menu': '˜‰ˆËʉÍÊ', + 'info': 'ȈÏØʉÍÊ', + 'kind': 'Ê' + } ), + 'completion_start_column': 13 + } ) + ) diff --git a/python/ycm/tests/test_utils.py b/python/ycm/tests/test_utils.py index 72f62962..468b48ca 100644 --- a/python/ycm/tests/test_utils.py +++ b/python/ycm/tests/test_utils.py @@ -23,6 +23,7 @@ from __future__ import absolute_import # Not installing aliases from python-future; it's unreliable and slow. from builtins import * # noqa +from future.utils import PY2 from mock import MagicMock, patch from hamcrest import assert_that, equal_to import contextlib @@ -32,7 +33,7 @@ import os import re import sys -from ycmd.utils import GetCurrentDirectory, ToUnicode +from ycmd.utils import GetCurrentDirectory, ToBytes, ToUnicode BUFNR_REGEX = re.compile( '^bufnr\(\'(?P.+)\', ([01])\)$' ) @@ -44,6 +45,8 @@ GETBUFVAR_REGEX = re.compile( MATCHADD_REGEX = re.compile( '^matchadd\(\'(?P.+)\', \'(?P.+)\'\)$' ) MATCHDELETE_REGEX = re.compile( '^matchdelete\((?P\d+)\)$' ) +OMNIFUNC_REGEX_FORMAT = ( + '^{omnifunc_name}\((?P[01]),[\'"](?P.*)[\'"]\)$' ) # One-and only instance of mocked Vim object. The first 'import vim' that is # executed binds the vim module to the instance of MagicMock that is created, @@ -99,7 +102,7 @@ def _MockGetBufferVariable( buffer_number, option ): def _MockVimBufferEval( value ): if value == '&omnifunc': - return VIM_MOCK.current.buffer.omnifunc + return VIM_MOCK.current.buffer.omnifunc_name if value == '&filetype': return VIM_MOCK.current.buffer.filetype @@ -120,6 +123,16 @@ def _MockVimBufferEval( value ): option = match.group( 'option' ) return _MockGetBufferVariable( buffer_number, option ) + current_buffer = VIM_MOCK.current.buffer + match = re.search( OMNIFUNC_REGEX_FORMAT.format( + omnifunc_name = current_buffer.omnifunc_name ), + value ) + if match: + findstart = int( match.group( 'findstart' ) ) + base = match.group( 'base' ) + value = current_buffer.omnifunc( findstart, base ) + return value if findstart else ToBytesOnPY2( value ) + return None @@ -221,16 +234,24 @@ class VimBuffer( object ): - |modified| : True if the buffer has unsaved changes, False otherwise; - |bufhidden|: value of the 'bufhidden' option (see :h bufhidden); - |window| : number of the buffer window. None if the buffer is hidden; - - |omnifunc| : omni completion function used by the buffer.""" + - |omnifunc| : omni completion function used by the buffer. Must be a Python + function that takes the same arguments and returns the same + values as a Vim completion function (:h complete-functions). + Example: + + def Omnifunc( findstart, base ): + if findstart: + return 5 + return [ 'a', 'b', 'c' ]""" def __init__( self, name, number = 1, - contents = [], + contents = [ '' ], filetype = '', modified = False, bufhidden = '', window = None, - omnifunc = '' ): + omnifunc = None ): self.name = os.path.realpath( name ) if name else '' self.number = number self.contents = contents @@ -239,6 +260,7 @@ class VimBuffer( object ): self.bufhidden = bufhidden self.window = window self.omnifunc = omnifunc + self.omnifunc_name = omnifunc.__name__ if omnifunc else '' self.changedtick = 1 @@ -292,10 +314,13 @@ def MockVimBuffers( buffers, current_buffer, cursor_position = ( 1, 1 ) ): if current_buffer not in buffers: raise RuntimeError( 'Current buffer must be part of the buffers list.' ) + line = current_buffer.contents[ cursor_position[ 0 ] - 1 ] + with patch( 'vim.buffers', buffers ): with patch( 'vim.current.buffer', current_buffer ): with patch( 'vim.current.window.cursor', cursor_position ): - yield VIM_MOCK + with patch( 'vim.current.line', line ): + yield VIM_MOCK def MockVimModule(): @@ -395,3 +420,20 @@ def ExpectedFailure( reason, *exception_matchers ): return Wrapper return decorator + + +def ToBytesOnPY2( data ): + # To test the omnifunc, etc. returning strings, which can be of different + # types depending on python version, we use ToBytes on PY2 and just the native + # str on python3. This roughly matches what happens between py2 and py3 + # versions within Vim. + if not PY2: + return data + + if isinstance( data, list ): + return [ ToBytesOnPY2( item ) for item in data ] + if isinstance( data, dict ): + for item in data: + data[ item ] = ToBytesOnPY2( data[ item ] ) + return data + return ToBytes( data )