diff --git a/python/ycm/tests/__init__.py b/python/ycm/tests/__init__.py index 76cc7280..8c2943c2 100644 --- a/python/ycm/tests/__init__.py +++ b/python/ycm/tests/__init__.py @@ -29,11 +29,12 @@ import functools import os import requests import time +import warnings from ycm.client.base_request import BaseRequest from ycm.youcompleteme import YouCompleteMe from ycmd import user_options_store -from ycmd.utils import WaitUntilProcessIsTerminated +from ycmd.utils import CloseStandardStreams, WaitUntilProcessIsTerminated # The default options which are only relevant to the client, not the server and # thus are not part of default_options.json, but are required for a working @@ -84,10 +85,23 @@ def StopServer( ycm ): try: ycm.OnVimLeave() WaitUntilProcessIsTerminated( ycm._server_popen ) + CloseStandardStreams( ycm._server_popen ) except Exception: pass +def setUpPackage(): + # We treat warnings as errors in our tests because warnings raised inside Vim + # will interrupt user workflow with a traceback and we don't want that. + warnings.filterwarnings( 'error' ) + # We ignore warnings from nose as we are not interested in them. + warnings.filterwarnings( 'ignore', module = 'nose' ) + + +def tearDownPackage(): + warnings.resetwarnings() + + def YouCompleteMeInstance( custom_options = {} ): """Defines a decorator function for tests that passes a unique YouCompleteMe instance as a parameter. This instance is initialized with the default options diff --git a/python/ycm/tests/test_utils.py b/python/ycm/tests/test_utils.py index 2b804f6f..049874df 100644 --- a/python/ycm/tests/test_utils.py +++ b/python/ycm/tests/test_utils.py @@ -91,6 +91,8 @@ def _MockGetBufferVariable( buffer_number, option ): return vim_buffer.filetype if option == 'changedtick': return vim_buffer.changedtick + if option == '&hid': + return vim_buffer.hidden return '' return '' @@ -214,6 +216,7 @@ class VimBuffer( object ): - |contents|: list of lines representing the buffer contents; - |filetype|: buffer filetype. Empty string if no filetype is set; - |modified|: True if the buffer has unsaved changes, False otherwise; + - |hidden| : True if the buffer is hidden, False otherwise; - |window| : number of the buffer window. None if the buffer is hidden; - |omnifunc|: omni completion function used by the buffer.""" @@ -222,6 +225,7 @@ class VimBuffer( object ): contents = [], filetype = '', modified = True, + hidden = False, window = None, omnifunc = '' ): self.name = os.path.realpath( name ) if name else '' @@ -229,7 +233,8 @@ class VimBuffer( object ): self.contents = contents self.filetype = filetype self.modified = modified - self.window = window + self.hidden = hidden + self.window = window if not hidden else None self.omnifunc = omnifunc self.changedtick = 1 @@ -287,7 +292,7 @@ def MockVimBuffers( buffers, current_buffer, cursor_position = ( 1, 1 ) ): with patch( 'vim.buffers', buffers ): with patch( 'vim.current.buffer', current_buffer ): with patch( 'vim.current.window.cursor', cursor_position ): - yield + yield VIM_MOCK def MockVimModule(): @@ -319,6 +324,16 @@ def MockVimModule(): return VIM_MOCK +class VimError( Exception ): + + def __init__( self, code ): + self.code = code + + + def __str__( self ): + return repr( self.code ) + + class ExtendedMock( MagicMock ): """An extension to the MagicMock class which adds the ability to check that a callable is called with a precise set of calls in a precise order. diff --git a/python/ycm/tests/vimsupport_test.py b/python/ycm/tests/vimsupport_test.py index 5f9684dc..9de6f6ee 100644 --- a/python/ycm/tests/vimsupport_test.py +++ b/python/ycm/tests/vimsupport_test.py @@ -27,7 +27,7 @@ from builtins import * # noqa from ycm.tests import PathToTestFile from ycm.tests.test_utils import ( CurrentWorkingDirectory, ExtendedMock, MockVimBuffers, MockVimCommand, - MockVimModule, VimBuffer ) + MockVimModule, VimBuffer, VimError ) MockVimModule() from ycm import vimsupport @@ -1602,3 +1602,138 @@ def EscapedFilepath_test(): '/path/\ with\ /sp\ ac\ es' ) eq_( vimsupport.EscapedFilepath( ' relative path/ with / spaces ' ), '\ relative\ path/\ with\ /\ spaces\ ' ) + + +@patch( 'ycmd.user_options_store._USER_OPTIONS', + { 'goto_buffer_command': 'same-buffer' } ) +@patch( 'vim.command', new_callable = ExtendedMock ) +def JumpToLocation_SameFile_SameBuffer_NoSwapFile_test( vim_command ): + # No 'u' prefix for the current buffer name string to simulate Vim returning + # bytes on Python 2 but unicode on Python 3. + current_buffer = VimBuffer( 'uni¢𐍈d€' ) + with MockVimBuffers( [ current_buffer ], current_buffer ) as vim: + vimsupport.JumpToLocation( os.path.realpath( u'uni¢𐍈d€' ), 2, 5 ) + + assert_that( vim.current.window.cursor, equal_to( ( 2, 4 ) ) ) + vim_command.assert_has_exact_calls( [ + call( 'normal! m\'' ), + call( 'normal! zz' ) + ] ) + + +@patch( 'ycmd.user_options_store._USER_OPTIONS', + { 'goto_buffer_command': 'same-buffer' } ) +@patch( 'vim.command', new_callable = ExtendedMock ) +def JumpToLocation_DifferentFile_SameBuffer_NoSwapFile_test( vim_command ): + current_buffer = VimBuffer( 'uni¢𐍈d€' ) + with MockVimBuffers( [ current_buffer ], current_buffer ) as vim: + target_name = os.path.realpath( u'different_uni¢𐍈d€' ) + + vimsupport.JumpToLocation( target_name, 2, 5 ) + + assert_that( vim.current.window.cursor, equal_to( ( 2, 4 ) ) ) + vim_command.assert_has_exact_calls( [ + call( 'normal! m\'' ), + call( u'keepjumps split {0}'.format( target_name ) ), + call( 'normal! zz' ) + ] ) + + +@patch( 'ycmd.user_options_store._USER_OPTIONS', + { 'goto_buffer_command': 'same-buffer' } ) +@patch( 'vim.error', VimError ) +@patch( 'vim.command', + side_effect = [ None, VimError( 'Unknown code' ), None ] ) +def JumpToLocation_DifferentFile_SameBuffer_SwapFile_Unexpected_test( + vim_command ): + + current_buffer = VimBuffer( 'uni¢𐍈d€' ) + with MockVimBuffers( [ current_buffer ], current_buffer ): + assert_that( + calling( vimsupport.JumpToLocation ).with_args( + os.path.realpath( u'different_uni¢𐍈d€' ), 2, 5 ), + raises( VimError, 'Unknown code' ) + ) + + +@patch( 'ycmd.user_options_store._USER_OPTIONS', + { 'goto_buffer_command': 'same-buffer' } ) +@patch( 'vim.error', VimError ) +@patch( 'vim.command', + new_callable = ExtendedMock, + side_effect = [ None, VimError( 'E325' ), None ] ) +def JumpToLocation_DifferentFile_SameBuffer_SwapFile_Quit_test( vim_command ): + current_buffer = VimBuffer( 'uni¢𐍈d€' ) + with MockVimBuffers( [ current_buffer ], current_buffer ): + target_name = os.path.realpath( u'different_uni¢𐍈d€' ) + + vimsupport.JumpToLocation( target_name, 2, 5 ) + + vim_command.assert_has_exact_calls( [ + call( 'normal! m\'' ), + call( u'keepjumps split {0}'.format( target_name ) ) + ] ) + + +@patch( 'ycmd.user_options_store._USER_OPTIONS', + { 'goto_buffer_command': 'same-buffer' } ) +@patch( 'vim.error', VimError ) +@patch( 'vim.command', + new_callable = ExtendedMock, + side_effect = [ None, KeyboardInterrupt, None ] ) +def JumpToLocation_DifferentFile_SameBuffer_SwapFile_Abort_test( vim_command ): + current_buffer = VimBuffer( 'uni¢𐍈d€' ) + with MockVimBuffers( [ current_buffer ], current_buffer ): + target_name = os.path.realpath( u'different_uni¢𐍈d€' ) + + vimsupport.JumpToLocation( target_name, 2, 5 ) + + vim_command.assert_has_exact_calls( [ + call( 'normal! m\'' ), + call( u'keepjumps split {0}'.format( target_name ) ) + ] ) + + +@patch( 'ycmd.user_options_store._USER_OPTIONS', + { 'goto_buffer_command': 'new-or-existing-tab' } ) +@patch( 'vim.command', new_callable = ExtendedMock ) +def JumpToLocation_DifferentFile_NewOrExistingTab_NotAlreadyOpened_test( + vim_command ): + + current_buffer = VimBuffer( 'uni¢𐍈d€' ) + with MockVimBuffers( [ current_buffer ], current_buffer ): + target_name = os.path.realpath( u'different_uni¢𐍈d€' ) + + vimsupport.JumpToLocation( target_name, 2, 5 ) + + vim_command.assert_has_exact_calls( [ + call( 'normal! m\'' ), + call( u'keepjumps tabedit {0}'.format( target_name ) ), + call( 'normal! zz' ) + ] ) + + +@patch( 'ycmd.user_options_store._USER_OPTIONS', + { 'goto_buffer_command': 'new-or-existing-tab' } ) +@patch( 'vim.command', new_callable = ExtendedMock ) +def JumpToLocation_DifferentFile_NewOrExistingTab_AlreadyOpened_test( + vim_command ): + + current_buffer = VimBuffer( 'uni¢𐍈d€' ) + different_buffer = VimBuffer( 'different_uni¢𐍈d€' ) + current_window = MagicMock( buffer = current_buffer ) + different_window = MagicMock( buffer = different_buffer ) + current_tab = MagicMock( windows = [ current_window, different_window ] ) + with patch( 'vim.tabpages', [ current_tab ] ): + with MockVimBuffers( [ current_buffer, different_buffer ], + current_buffer ) as vim: + vimsupport.JumpToLocation( os.path.realpath( u'different_uni¢𐍈d€' ), + 2, 5 ) + + assert_that( vim.current.tabpage, equal_to( current_tab ) ) + assert_that( vim.current.window, equal_to( different_window ) ) + assert_that( vim.current.window.cursor, equal_to( ( 2, 4 ) ) ) + vim_command.assert_has_exact_calls( [ + call( 'normal! m\'' ), + call( 'normal! zz' ) + ] )