aboutsummaryrefslogtreecommitdiff
path: root/vim/bundle/YouCompleteMe/python/ycm/test_utils.py
diff options
context:
space:
mode:
Diffstat (limited to 'vim/bundle/YouCompleteMe/python/ycm/test_utils.py')
-rw-r--r--vim/bundle/YouCompleteMe/python/ycm/test_utils.py201
1 files changed, 201 insertions, 0 deletions
diff --git a/vim/bundle/YouCompleteMe/python/ycm/test_utils.py b/vim/bundle/YouCompleteMe/python/ycm/test_utils.py
new file mode 100644
index 0000000..3a16255
--- /dev/null
+++ b/vim/bundle/YouCompleteMe/python/ycm/test_utils.py
@@ -0,0 +1,201 @@
+# Copyright (C) 2011, 2012 Google Inc.
+#
+# This file is part of YouCompleteMe.
+#
+# YouCompleteMe 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.
+#
+# YouCompleteMe 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 YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
+
+from __future__ import unicode_literals
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+from future import standard_library
+standard_library.install_aliases()
+from builtins import * # noqa
+
+from mock import MagicMock
+from hamcrest import assert_that, equal_to
+import re
+import sys
+import nose
+import functools
+
+from ycmd.utils import ToUnicode
+
+
+BUFNR_REGEX = re.compile( r"^bufnr\('(.+)', ([0-9]+)\)$" )
+BUFWINNR_REGEX = re.compile( r"^bufwinnr\(([0-9]+)\)$" )
+BWIPEOUT_REGEX = re.compile( r"^(?:silent! )bwipeout!? ([0-9]+)$" )
+
+# 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,
+# and subsquent assignments to sys.modules[ 'vim' ] don't retrospectively
+# update them. The result is that while running the tests, we must assign only
+# one instance of MagicMock to sys.modules[ 'vim' ] and always return it.
+#
+# More explanation is available:
+# https://github.com/Valloric/YouCompleteMe/pull/1694
+VIM_MOCK = MagicMock()
+
+# 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
+# YouCompleteMe or OmniCompleter object.
+DEFAULT_CLIENT_OPTIONS = {
+ 'server_log_level': 'info',
+ 'extra_conf_vim_data': [],
+ 'show_diagnostics_ui': 1,
+ 'enable_diagnostic_signs': 1,
+ 'enable_diagnostic_highlighting': 0,
+ 'always_populate_location_list': 0,
+}
+
+
+def MockGetBufferNumber( buffer_filename ):
+ for buffer in VIM_MOCK.buffers:
+ if buffer[ 'filename' ] == buffer_filename:
+ return buffer[ 'number' ]
+ return -1
+
+
+def MockGetBufferWindowNumber( buffer_number ):
+ for buffer in VIM_MOCK.buffers:
+ if buffer[ 'number' ] == buffer_number and 'window' in buffer:
+ return buffer[ 'window' ]
+ return -1
+
+
+def MockVimEval( value ):
+ if value == "g:ycm_min_num_of_chars_for_completion":
+ return 0
+ if value == "g:ycm_server_python_interpreter":
+ return ''
+ if value == "tempname()":
+ return '_TEMP_FILE_'
+ if value == "&previewheight":
+ # Default value from Vim
+ return 12
+
+ match = BUFNR_REGEX.search( value )
+ if match:
+ return MockGetBufferNumber( match.group( 1 ) )
+
+ match = BUFWINNR_REGEX.search( value )
+ if match:
+ return MockGetBufferWindowNumber( int( match.group( 1 ) ) )
+
+ raise ValueError( 'Unexpected evaluation: ' + value )
+
+
+def MockWipeoutBuffer( buffer_number ):
+ buffers = VIM_MOCK.buffers
+
+ for index, buffer in enumerate( buffers ):
+ if buffer[ 'number' ] == buffer_number:
+ return buffers.pop( index )
+
+
+def MockVimCommand( command ):
+ match = BWIPEOUT_REGEX.search( command )
+ if match:
+ return MockWipeoutBuffer( int( match.group( 1 ) ) )
+
+ raise RuntimeError( 'Unexpected command: ' + command )
+
+
+def MockVimModule():
+ """The 'vim' module is something that is only present when running inside the
+ Vim Python interpreter, so we replace it with a MagicMock for tests. If you
+ need to add additional mocks to vim module functions, then use 'patch' from
+ mock module, to ensure that the state of the vim mock is returned before the
+ next test. That is:
+
+ from ycm.test_utils import MockVimModule
+ from mock import patch
+
+ # Do this once
+ MockVimModule()
+
+ @patch( 'vim.eval', return_value='test' )
+ @patch( 'vim.command', side_effect=ValueError )
+ def test( vim_command, vim_eval ):
+ # use vim.command via vim_command, e.g.:
+ vim_command.assert_has_calls( ... )
+
+ Failure to use this approach may lead to unexpected failures in other
+ tests."""
+
+ VIM_MOCK.buffers = {}
+ VIM_MOCK.eval = MagicMock( side_effect = MockVimEval )
+ sys.modules[ 'vim' ] = VIM_MOCK
+
+ return VIM_MOCK
+
+
+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.
+
+ Example Usage:
+ from ycm.test_utils import ExtendedMock
+ @patch( 'test.testing', new_callable = ExtendedMock, ... )
+ def my_test( test_testing ):
+ ...
+ """
+
+ def assert_has_exact_calls( self, calls, any_order = False ):
+ self.assert_has_calls( calls, any_order )
+ assert_that( self.call_count, equal_to( len( calls ) ) )
+
+
+def ExpectedFailure( reason, *exception_matchers ):
+ """Defines a decorator to be attached to tests. This decorator
+ marks the test as being known to fail, e.g. where documenting or exercising
+ known incorrect behaviour.
+
+ The parameters are:
+ - |reason| a textual description of the reason for the known issue. This
+ is used for the skip reason
+ - |exception_matchers| additional arguments are hamcrest matchers to apply
+ to the exception thrown. If the matchers don't match, then the
+ test is marked as error, with the original exception.
+
+ If the test fails (for the correct reason), then it is marked as skipped.
+ If it fails for any other reason, it is marked as failed.
+ If the test passes, then it is also marked as failed."""
+ def decorator( test ):
+ @functools.wraps( test )
+ def Wrapper( *args, **kwargs ):
+ try:
+ test( *args, **kwargs )
+ except Exception as test_exception:
+ # Ensure that we failed for the right reason
+ test_exception_message = ToUnicode( test_exception )
+ try:
+ for matcher in exception_matchers:
+ assert_that( test_exception_message, matcher )
+ except AssertionError:
+ # Failed for the wrong reason!
+ import traceback
+ print( 'Test failed for the wrong reason: ' + traceback.format_exc() )
+ # Real failure reason is the *original* exception, we're only trapping
+ # and ignoring the exception that is expected.
+ raise test_exception
+
+ # Failed for the right reason
+ raise nose.SkipTest( reason )
+ else:
+ raise AssertionError( 'Test was expected to fail: {0}'.format(
+ reason ) )
+ return Wrapper
+
+ return decorator