From 3f4b3238bf3385f39f52b4796e4ad31744e0c00c Mon Sep 17 00:00:00 2001 From: Boris Staletic Date: Fri, 10 Sep 2021 06:52:51 +0200 Subject: [PATCH] Port to unittest --- pytest.ini | 7 - python/test_requirements.txt | 2 - python/ycm/tests/__init__.py | 111 +- python/ycm/tests/base_test.py | 360 +- python/ycm/tests/client/base_request_test.py | 28 +- .../ycm/tests/client/command_request_test.py | 123 +- .../tests/client/completion_request_test.py | 29 +- .../tests/client/debug_info_request_test.py | 195 +- .../ycm/tests/client/messages_request_test.py | 220 +- .../client/omni_completion_request_test.py | 26 +- python/ycm/tests/command_test.py | 225 +- python/ycm/tests/completion_test.py | 789 ++-- python/ycm/tests/conftest.py | 143 - python/ycm/tests/diagnostic_filter_test.py | 118 +- python/ycm/tests/event_notification_test.py | 693 ++-- python/ycm/tests/omni_completer_test.py | 1763 ++++---- python/ycm/tests/paths_test.py | 45 +- python/ycm/tests/postcomplete_test.py | 559 +-- python/ycm/tests/signature_help_test.py | 30 +- python/ycm/tests/syntax_parse_test.py | 455 +-- python/ycm/tests/test_utils.py | 4 +- python/ycm/tests/vimsupport_test.py | 3561 +++++++++-------- python/ycm/tests/youcompleteme_test.py | 1998 ++++----- run_tests.py | 41 +- 24 files changed, 5808 insertions(+), 5717 deletions(-) delete mode 100644 pytest.ini delete mode 100644 python/ycm/tests/conftest.py diff --git a/pytest.ini b/pytest.ini deleted file mode 100644 index 5981153562..0000000000 --- a/pytest.ini +++ /dev/null @@ -1,7 +0,0 @@ -[pytest] -python_functions = *_test -python_classes = *_test -xfail_strict = true -log_level = debug -log_date_format = "%Y-%m-%d %H:%M:%S" -log_format = "%(asctime)s - %(levelname)s - %(message)s" diff --git a/python/test_requirements.txt b/python/test_requirements.txt index d3b500518d..3a0bc1e34c 100644 --- a/python/test_requirements.txt +++ b/python/test_requirements.txt @@ -6,5 +6,3 @@ codecov >= 2.0.5 coverage <5.0 click <8.0.0 covimerage >= 0.2.0 -pytest -pytest-cov diff --git a/python/ycm/tests/__init__.py b/python/ycm/tests/__init__.py index 3202f80b56..b5a85f9113 100644 --- a/python/ycm/tests/__init__.py +++ b/python/ycm/tests/__init__.py @@ -16,9 +16,118 @@ # along with YouCompleteMe. If not, see . import os -from ycm.tests.conftest import * # noqa +from ycm.tests.test_utils import MockVimModule +MockVimModule() + +import contextlib +import functools +import time +from urllib.error import HTTPError, URLError + +from ycm.client.base_request import BaseRequest +from ycm.tests import test_utils +from ycm.youcompleteme import YouCompleteMe +from ycmd.utils import CloseStandardStreams, WaitUntilProcessIsTerminated def PathToTestFile( *args ): dir_of_current_script = os.path.dirname( os.path.abspath( __file__ ) ) return os.path.join( dir_of_current_script, 'testdata', *args ) + + +# The default options which are required for a working YouCompleteMe object. +DEFAULT_CLIENT_OPTIONS = { + # YCM options + 'g:ycm_log_level': 'info', + 'g:ycm_keep_logfiles': 0, + 'g:ycm_extra_conf_vim_data': [], + 'g:ycm_server_python_interpreter': '', + 'g:ycm_show_diagnostics_ui': 1, + 'g:ycm_enable_diagnostic_signs': 1, + 'g:ycm_enable_diagnostic_highlighting': 0, + 'g:ycm_echo_current_diagnostic': 1, + 'g:ycm_filter_diagnostics': {}, + 'g:ycm_always_populate_location_list': 0, + 'g:ycm_collect_identifiers_from_tags_files': 0, + 'g:ycm_seed_identifiers_with_syntax': 0, + 'g:ycm_goto_buffer_command': 'same-buffer', + 'g:ycm_update_diagnostics_in_insert_mode': 1, + # ycmd options + 'g:ycm_auto_trigger': 1, + 'g:ycm_min_num_of_chars_for_completion': 2, + 'g:ycm_semantic_triggers': {}, + 'g:ycm_filetype_specific_completion_to_disable': { 'gitcommit': 1 }, + 'g:ycm_max_num_candidates': 50, + 'g:ycm_max_diagnostics_to_display': 30, + 'g:ycm_disable_signature_help': 0, +} + + +@contextlib.contextmanager +def UserOptions( options ): + old_vim_options = test_utils.VIM_OPTIONS.copy() + test_utils.VIM_OPTIONS.update( DEFAULT_CLIENT_OPTIONS ) + test_utils.VIM_OPTIONS.update( options ) + try: + yield + finally: + test_utils.VIM_OPTIONS = old_vim_options + + +def _IsReady(): + return BaseRequest().GetDataFromHandler( 'ready' ) + + +def WaitUntilReady( timeout = 5 ): + expiration = time.time() + timeout + while True: + try: + if time.time() > expiration: + raise RuntimeError( 'Waited for the server to be ready ' + f'for { timeout } seconds, aborting.' ) + if _IsReady(): + return + except ( URLError, HTTPError ): + pass + finally: + time.sleep( 0.1 ) + + +def StopServer( ycm ): + try: + ycm.OnVimLeave() + WaitUntilProcessIsTerminated( ycm._server_popen ) + CloseStandardStreams( ycm._server_popen ) + except Exception: + pass + + +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 + `DEFAULT_CLIENT_OPTIONS`. Use the optional parameter |custom_options| to give + additional options and/or override the already existing ones. + + Example usage: + + from ycm.tests import YouCompleteMeInstance + + @YouCompleteMeInstance( { 'log_level': 'debug', + 'keep_logfiles': 1 } ) + def Debug_test( ycm ): + ... + """ + def Decorator( test ): + @functools.wraps( test ) + def Wrapper( *args, **kwargs ): + with UserOptions( custom_options ): + ycm = YouCompleteMe() + WaitUntilReady() + ycm.CheckIfServerIsReady() + try: + test_utils.VIM_MATCHES_FOR_WINDOW.clear() + return test( args[ 0 ], ycm, *args[ 1: ], **kwargs ) + finally: + StopServer( ycm ) + return Wrapper + return Decorator diff --git a/python/ycm/tests/base_test.py b/python/ycm/tests/base_test.py index 82e2e7d6fd..534ce4eaf6 100644 --- a/python/ycm/tests/base_test.py +++ b/python/ycm/tests/base_test.py @@ -18,6 +18,7 @@ import contextlib from hamcrest import assert_that, equal_to +from unittest import TestCase from unittest.mock import patch from ycm.tests.test_utils import MockVimModule @@ -45,263 +46,264 @@ def MockTextAfterCursor( text ): yield -def AdjustCandidateInsertionText_Basic_test(): - with MockTextAfterCursor( 'bar' ): - assert_that( [ { 'word': 'foo', 'abbr': 'foobar' } ], - equal_to( base.AdjustCandidateInsertionText( [ - { 'word': 'foobar', 'abbr': '' } ] ) ) ) +class BaseTest( TestCase ): + def test_AdjustCandidateInsertionText_Basic( self ): + with MockTextAfterCursor( 'bar' ): + assert_that( [ { 'word': 'foo', 'abbr': 'foobar' } ], + equal_to( base.AdjustCandidateInsertionText( [ + { 'word': 'foobar', 'abbr': '' } ] ) ) ) -def AdjustCandidateInsertionText_ParenInTextAfterCursor_test(): - with MockTextAfterCursor( 'bar(zoo' ): - assert_that( [ { 'word': 'foo', 'abbr': 'foobar' } ], - equal_to( base.AdjustCandidateInsertionText( [ - { 'word': 'foobar', 'abbr': '' } ] ) ) ) + def test_AdjustCandidateInsertionText_ParenInTextAfterCursor( self ): + with MockTextAfterCursor( 'bar(zoo' ): + assert_that( [ { 'word': 'foo', 'abbr': 'foobar' } ], + equal_to( base.AdjustCandidateInsertionText( [ + { 'word': 'foobar', 'abbr': '' } ] ) ) ) -def AdjustCandidateInsertionText_PlusInTextAfterCursor_test(): - with MockTextAfterCursor( 'bar+zoo' ): - assert_that( [ { 'word': 'foo', 'abbr': 'foobar' } ], - equal_to( base.AdjustCandidateInsertionText( [ - { 'word': 'foobar', 'abbr': '' } ] ) ) ) + def test_AdjustCandidateInsertionText_PlusInTextAfterCursor( self ): + with MockTextAfterCursor( 'bar+zoo' ): + assert_that( [ { 'word': 'foo', 'abbr': 'foobar' } ], + equal_to( base.AdjustCandidateInsertionText( [ + { 'word': 'foobar', 'abbr': '' } ] ) ) ) -def AdjustCandidateInsertionText_WhitespaceInTextAfterCursor_test(): - with MockTextAfterCursor( 'bar zoo' ): - assert_that( [ { 'word': 'foo', 'abbr': 'foobar' } ], - equal_to( base.AdjustCandidateInsertionText( [ - { 'word': 'foobar', 'abbr': '' } ] ) ) ) + def test_AdjustCandidateInsertionText_WhitespaceInTextAfterCursor( self ): + with MockTextAfterCursor( 'bar zoo' ): + assert_that( [ { 'word': 'foo', 'abbr': 'foobar' } ], + equal_to( base.AdjustCandidateInsertionText( [ + { 'word': 'foobar', 'abbr': '' } ] ) ) ) -def AdjustCandidateInsertionText_MoreThanWordMatchingAfterCursor_test(): - with MockTextAfterCursor( 'bar.h' ): - assert_that( [ { 'word': 'foo', 'abbr': 'foobar.h' } ], - equal_to( base.AdjustCandidateInsertionText( [ - { 'word': 'foobar.h', 'abbr': '' } ] ) ) ) + def test_AdjustCandidateInsertionText_MoreThanWordMatchingAfterCursor( self ): + with MockTextAfterCursor( 'bar.h' ): + assert_that( [ { 'word': 'foo', 'abbr': 'foobar.h' } ], + equal_to( base.AdjustCandidateInsertionText( [ + { 'word': 'foobar.h', 'abbr': '' } ] ) ) ) - with MockTextAfterCursor( 'bar(zoo' ): - assert_that( [ { 'word': 'foo', 'abbr': 'foobar(zoo' } ], - equal_to( base.AdjustCandidateInsertionText( [ - { 'word': 'foobar(zoo', 'abbr': '' } ] ) ) ) + with MockTextAfterCursor( 'bar(zoo' ): + assert_that( [ { 'word': 'foo', 'abbr': 'foobar(zoo' } ], + equal_to( base.AdjustCandidateInsertionText( [ + { 'word': 'foobar(zoo', 'abbr': '' } ] ) ) ) -def AdjustCandidateInsertionText_NotSuffix_test(): - with MockTextAfterCursor( 'bar' ): - assert_that( [ { 'word': 'foofoo', 'abbr': 'foofoo' } ], - equal_to( base.AdjustCandidateInsertionText( [ - { 'word': 'foofoo', 'abbr': '' } ] ) ) ) + def test_AdjustCandidateInsertionText_NotSuffix( self ): + with MockTextAfterCursor( 'bar' ): + assert_that( [ { 'word': 'foofoo', 'abbr': 'foofoo' } ], + equal_to( base.AdjustCandidateInsertionText( [ + { 'word': 'foofoo', 'abbr': '' } ] ) ) ) -def AdjustCandidateInsertionText_NothingAfterCursor_test(): - with MockTextAfterCursor( '' ): - assert_that( [ { 'word': 'foofoo', 'abbr': '' }, - { 'word': 'zobar', 'abbr': '' } ], - equal_to( base.AdjustCandidateInsertionText( [ - { 'word': 'foofoo', 'abbr': '' }, - { 'word': 'zobar', 'abbr': '' } ] ) ) ) + def test_AdjustCandidateInsertionText_NothingAfterCursor( self ): + with MockTextAfterCursor( '' ): + assert_that( [ { 'word': 'foofoo', 'abbr': '' }, + { 'word': 'zobar', 'abbr': '' } ], + equal_to( base.AdjustCandidateInsertionText( [ + { 'word': 'foofoo', 'abbr': '' }, + { 'word': 'zobar', 'abbr': '' } ] ) ) ) -def AdjustCandidateInsertionText_MultipleStrings_test(): - with MockTextAfterCursor( 'bar' ): - assert_that( [ { 'word': 'foo', 'abbr': 'foobar' }, - { 'word': 'zo', 'abbr': 'zobar' }, - { 'word': 'q', 'abbr': 'qbar' }, - { 'word': '', 'abbr': 'bar' }, ], - equal_to( base.AdjustCandidateInsertionText( [ - { 'word': 'foobar', 'abbr': '' }, - { 'word': 'zobar', 'abbr': '' }, - { 'word': 'qbar', 'abbr': '' }, - { 'word': 'bar', 'abbr': '' } ] ) ) ) + def test_AdjustCandidateInsertionText_MultipleStrings( self ): + with MockTextAfterCursor( 'bar' ): + assert_that( [ { 'word': 'foo', 'abbr': 'foobar' }, + { 'word': 'zo', 'abbr': 'zobar' }, + { 'word': 'q', 'abbr': 'qbar' }, + { 'word': '', 'abbr': 'bar' }, ], + equal_to( base.AdjustCandidateInsertionText( [ + { 'word': 'foobar', 'abbr': '' }, + { 'word': 'zobar', 'abbr': '' }, + { 'word': 'qbar', 'abbr': '' }, + { 'word': 'bar', 'abbr': '' } ] ) ) ) -def AdjustCandidateInsertionText_DontTouchAbbr_test(): - with MockTextAfterCursor( 'bar' ): - assert_that( [ { 'word': 'foo', 'abbr': '1234' } ], - equal_to( base.AdjustCandidateInsertionText( [ - { 'word': 'foobar', 'abbr': '1234' } ] ) ) ) + def test_AdjustCandidateInsertionText_DontTouchAbbr( self ): + with MockTextAfterCursor( 'bar' ): + assert_that( [ { 'word': 'foo', 'abbr': '1234' } ], + equal_to( base.AdjustCandidateInsertionText( [ + { 'word': 'foobar', 'abbr': '1234' } ] ) ) ) -def AdjustCandidateInsertionText_NoAbbr_test(): - with MockTextAfterCursor( 'bar' ): - assert_that( [ { 'word': 'foo', 'abbr': 'foobar' } ], - equal_to( base.AdjustCandidateInsertionText( [ - { 'word': 'foobar' } ] ) ) ) + def test_AdjustCandidateInsertionText_NoAbbr( self ): + with MockTextAfterCursor( 'bar' ): + assert_that( [ { 'word': 'foo', 'abbr': 'foobar' } ], + equal_to( base.AdjustCandidateInsertionText( [ + { 'word': 'foobar' } ] ) ) ) -def OverlapLength_Basic_test(): - assert_that( 3, equal_to( base.OverlapLength( 'foo bar', 'bar zoo' ) ) ) - assert_that( 3, equal_to( base.OverlapLength( 'foobar', 'barzoo' ) ) ) + def test_OverlapLength_Basic( self ): + assert_that( 3, equal_to( base.OverlapLength( 'foo bar', 'bar zoo' ) ) ) + assert_that( 3, equal_to( base.OverlapLength( 'foobar', 'barzoo' ) ) ) -def OverlapLength_BasicWithUnicode_test(): - assert_that( 3, equal_to( base.OverlapLength( 'bar fäö', 'fäö bar' ) ) ) - assert_that( 3, equal_to( base.OverlapLength( 'zoofäö', 'fäözoo' ) ) ) + def test_OverlapLength_BasicWithUnicode( self ): + assert_that( 3, equal_to( base.OverlapLength( 'bar fäö', 'fäö bar' ) ) ) + assert_that( 3, equal_to( base.OverlapLength( 'zoofäö', 'fäözoo' ) ) ) -def OverlapLength_OneCharOverlap_test(): - assert_that( 1, equal_to( base.OverlapLength( 'foo b', 'b zoo' ) ) ) + def test_OverlapLength_OneCharOverlap( self ): + assert_that( 1, equal_to( base.OverlapLength( 'foo b', 'b zoo' ) ) ) -def OverlapLength_SameStrings_test(): - assert_that( 6, equal_to( base.OverlapLength( 'foobar', 'foobar' ) ) ) + def test_OverlapLength_SameStrings( self ): + assert_that( 6, equal_to( base.OverlapLength( 'foobar', 'foobar' ) ) ) -def OverlapLength_Substring_test(): - assert_that( 6, equal_to( base.OverlapLength( 'foobar', 'foobarzoo' ) ) ) - assert_that( 6, equal_to( base.OverlapLength( 'zoofoobar', 'foobar' ) ) ) + def test_OverlapLength_Substring( self ): + assert_that( 6, equal_to( base.OverlapLength( 'foobar', 'foobarzoo' ) ) ) + assert_that( 6, equal_to( base.OverlapLength( 'zoofoobar', 'foobar' ) ) ) -def OverlapLength_LongestOverlap_test(): - assert_that( 7, equal_to( base.OverlapLength( 'bar foo foo', - 'foo foo bar' ) ) ) + def test_OverlapLength_LongestOverlap( self ): + assert_that( 7, equal_to( base.OverlapLength( 'bar foo foo', + 'foo foo bar' ) ) ) -def OverlapLength_EmptyInput_test(): - assert_that( 0, equal_to( base.OverlapLength( '', 'goobar' ) ) ) - assert_that( 0, equal_to( base.OverlapLength( 'foobar', '' ) ) ) - assert_that( 0, equal_to( base.OverlapLength( '', '' ) ) ) + def test_OverlapLength_EmptyInput( self ): + assert_that( 0, equal_to( base.OverlapLength( '', 'goobar' ) ) ) + assert_that( 0, equal_to( base.OverlapLength( 'foobar', '' ) ) ) + assert_that( 0, equal_to( base.OverlapLength( '', '' ) ) ) -def OverlapLength_NoOverlap_test(): - assert_that( 0, equal_to( base.OverlapLength( 'foobar', 'goobar' ) ) ) - assert_that( 0, equal_to( base.OverlapLength( 'foobar', '(^($@#$#@' ) ) ) - assert_that( 0, equal_to( base.OverlapLength( 'foo bar zoo', - 'foo zoo bar' ) ) ) + def test_OverlapLength_NoOverlap( self ): + assert_that( 0, equal_to( base.OverlapLength( 'foobar', 'goobar' ) ) ) + assert_that( 0, equal_to( base.OverlapLength( 'foobar', '(^($@#$#@' ) ) ) + assert_that( 0, equal_to( base.OverlapLength( 'foo bar zoo', + 'foo zoo bar' ) ) ) -def LastEnteredCharIsIdentifierChar_Basic_test(): - with MockCurrentFiletypes(): - with MockCurrentColumnAndLineContents( 3, 'abc' ): - assert_that( base.LastEnteredCharIsIdentifierChar() ) + def test_LastEnteredCharIsIdentifierChar_Basic( self ): + with MockCurrentFiletypes(): + with MockCurrentColumnAndLineContents( 3, 'abc' ): + assert_that( base.LastEnteredCharIsIdentifierChar() ) - with MockCurrentColumnAndLineContents( 2, 'abc' ): - assert_that( base.LastEnteredCharIsIdentifierChar() ) + with MockCurrentColumnAndLineContents( 2, 'abc' ): + assert_that( base.LastEnteredCharIsIdentifierChar() ) - with MockCurrentColumnAndLineContents( 1, 'abc' ): - assert_that( base.LastEnteredCharIsIdentifierChar() ) + with MockCurrentColumnAndLineContents( 1, 'abc' ): + assert_that( base.LastEnteredCharIsIdentifierChar() ) -def LastEnteredCharIsIdentifierChar_FiletypeHtml_test(): - with MockCurrentFiletypes( [ 'html' ] ): - with MockCurrentColumnAndLineContents( 3, 'ab-' ): - assert_that( base.LastEnteredCharIsIdentifierChar() ) + def test_LastEnteredCharIsIdentifierChar_FiletypeHtml( self ): + with MockCurrentFiletypes( [ 'html' ] ): + with MockCurrentColumnAndLineContents( 3, 'ab-' ): + assert_that( base.LastEnteredCharIsIdentifierChar() ) -def LastEnteredCharIsIdentifierChar_ColumnIsZero_test(): - with MockCurrentColumnAndLineContents( 0, 'abc' ): - assert_that( not base.LastEnteredCharIsIdentifierChar() ) - - -def LastEnteredCharIsIdentifierChar_LineEmpty_test(): - with MockCurrentFiletypes(): - with MockCurrentColumnAndLineContents( 3, '' ): + def test_LastEnteredCharIsIdentifierChar_ColumnIsZero( self ): + with MockCurrentColumnAndLineContents( 0, 'abc' ): assert_that( not base.LastEnteredCharIsIdentifierChar() ) - with MockCurrentColumnAndLineContents( 0, '' ): - assert_that( not base.LastEnteredCharIsIdentifierChar() ) + def test_LastEnteredCharIsIdentifierChar_LineEmpty( self ): + with MockCurrentFiletypes(): + with MockCurrentColumnAndLineContents( 3, '' ): + assert_that( not base.LastEnteredCharIsIdentifierChar() ) -def LastEnteredCharIsIdentifierChar_NotIdentChar_test(): - with MockCurrentFiletypes(): - with MockCurrentColumnAndLineContents( 3, 'ab;' ): - assert_that( not base.LastEnteredCharIsIdentifierChar() ) + with MockCurrentColumnAndLineContents( 0, '' ): + assert_that( not base.LastEnteredCharIsIdentifierChar() ) - with MockCurrentColumnAndLineContents( 1, ';' ): - assert_that( not base.LastEnteredCharIsIdentifierChar() ) - with MockCurrentColumnAndLineContents( 3, 'ab-' ): - assert_that( not base.LastEnteredCharIsIdentifierChar() ) + def test_LastEnteredCharIsIdentifierChar_NotIdentChar( self ): + with MockCurrentFiletypes(): + with MockCurrentColumnAndLineContents( 3, 'ab;' ): + assert_that( not base.LastEnteredCharIsIdentifierChar() ) + with MockCurrentColumnAndLineContents( 1, ';' ): + assert_that( not base.LastEnteredCharIsIdentifierChar() ) -def LastEnteredCharIsIdentifierChar_Unicode_test(): - with MockCurrentFiletypes(): - # CurrentColumn returns a byte offset and character ø is 2 bytes length. - with MockCurrentColumnAndLineContents( 5, 'føo(' ): - assert_that( not base.LastEnteredCharIsIdentifierChar() ) + with MockCurrentColumnAndLineContents( 3, 'ab-' ): + assert_that( not base.LastEnteredCharIsIdentifierChar() ) - with MockCurrentColumnAndLineContents( 4, 'føo(' ): - assert_that( base.LastEnteredCharIsIdentifierChar() ) - with MockCurrentColumnAndLineContents( 3, 'føo(' ): - assert_that( base.LastEnteredCharIsIdentifierChar() ) + def test_LastEnteredCharIsIdentifierChar_Unicode( self ): + with MockCurrentFiletypes(): + # CurrentColumn returns a byte offset and character ø is 2 bytes length. + with MockCurrentColumnAndLineContents( 5, 'føo(' ): + assert_that( not base.LastEnteredCharIsIdentifierChar() ) - with MockCurrentColumnAndLineContents( 1, 'føo(' ): - assert_that( base.LastEnteredCharIsIdentifierChar() ) + with MockCurrentColumnAndLineContents( 4, 'føo(' ): + assert_that( base.LastEnteredCharIsIdentifierChar() ) + with MockCurrentColumnAndLineContents( 3, 'føo(' ): + assert_that( base.LastEnteredCharIsIdentifierChar() ) -def CurrentIdentifierFinished_Basic_test(): - with MockCurrentFiletypes(): - with MockCurrentColumnAndLineContents( 3, 'ab;' ): - assert_that( base.CurrentIdentifierFinished() ) + with MockCurrentColumnAndLineContents( 1, 'føo(' ): + assert_that( base.LastEnteredCharIsIdentifierChar() ) - with MockCurrentColumnAndLineContents( 2, 'ab;' ): - assert_that( not base.CurrentIdentifierFinished() ) - with MockCurrentColumnAndLineContents( 1, 'ab;' ): - assert_that( not base.CurrentIdentifierFinished() ) + def test_CurrentIdentifierFinished_Basic( self ): + with MockCurrentFiletypes(): + with MockCurrentColumnAndLineContents( 3, 'ab;' ): + assert_that( base.CurrentIdentifierFinished() ) + with MockCurrentColumnAndLineContents( 2, 'ab;' ): + assert_that( not base.CurrentIdentifierFinished() ) -def CurrentIdentifierFinished_NothingBeforeColumn_test(): - with MockCurrentColumnAndLineContents( 0, 'ab;' ): - assert_that( base.CurrentIdentifierFinished() ) + with MockCurrentColumnAndLineContents( 1, 'ab;' ): + assert_that( not base.CurrentIdentifierFinished() ) - with MockCurrentColumnAndLineContents( 0, '' ): - assert_that( base.CurrentIdentifierFinished() ) + def test_CurrentIdentifierFinished_NothingBeforeColumn( self ): + with MockCurrentColumnAndLineContents( 0, 'ab;' ): + assert_that( base.CurrentIdentifierFinished() ) -def CurrentIdentifierFinished_InvalidColumn_test(): - with MockCurrentFiletypes(): - with MockCurrentColumnAndLineContents( 5, '' ): + with MockCurrentColumnAndLineContents( 0, '' ): assert_that( base.CurrentIdentifierFinished() ) - with MockCurrentColumnAndLineContents( 5, 'abc' ): - assert_that( not base.CurrentIdentifierFinished() ) - with MockCurrentColumnAndLineContents( 4, 'ab;' ): - assert_that( base.CurrentIdentifierFinished() ) + def test_CurrentIdentifierFinished_InvalidColumn( self ): + with MockCurrentFiletypes(): + with MockCurrentColumnAndLineContents( 5, '' ): + assert_that( base.CurrentIdentifierFinished() ) + with MockCurrentColumnAndLineContents( 5, 'abc' ): + assert_that( not base.CurrentIdentifierFinished() ) -def CurrentIdentifierFinished_InMiddleOfLine_test(): - with MockCurrentFiletypes(): - with MockCurrentColumnAndLineContents( 4, 'bar.zoo' ): - assert_that( base.CurrentIdentifierFinished() ) + with MockCurrentColumnAndLineContents( 4, 'ab;' ): + assert_that( base.CurrentIdentifierFinished() ) - with MockCurrentColumnAndLineContents( 4, 'bar(zoo' ): - assert_that( base.CurrentIdentifierFinished() ) - with MockCurrentColumnAndLineContents( 4, 'bar-zoo' ): - assert_that( base.CurrentIdentifierFinished() ) + def test_CurrentIdentifierFinished_InMiddleOfLine( self ): + with MockCurrentFiletypes(): + with MockCurrentColumnAndLineContents( 4, 'bar.zoo' ): + assert_that( base.CurrentIdentifierFinished() ) + with MockCurrentColumnAndLineContents( 4, 'bar(zoo' ): + assert_that( base.CurrentIdentifierFinished() ) -def CurrentIdentifierFinished_Html_test(): - with MockCurrentFiletypes( [ 'html' ] ): - with MockCurrentColumnAndLineContents( 4, 'bar-zoo' ): - assert_that( not base.CurrentIdentifierFinished() ) + with MockCurrentColumnAndLineContents( 4, 'bar-zoo' ): + assert_that( base.CurrentIdentifierFinished() ) -def CurrentIdentifierFinished_WhitespaceOnly_test(): - with MockCurrentFiletypes(): - with MockCurrentColumnAndLineContents( 1, '\n' ): - assert_that( base.CurrentIdentifierFinished() ) + def test_CurrentIdentifierFinished_Html( self ): + with MockCurrentFiletypes( [ 'html' ] ): + with MockCurrentColumnAndLineContents( 4, 'bar-zoo' ): + assert_that( not base.CurrentIdentifierFinished() ) - with MockCurrentColumnAndLineContents( 3, '\n ' ): - assert_that( base.CurrentIdentifierFinished() ) - with MockCurrentColumnAndLineContents( 3, '\t\t\t\t' ): - assert_that( base.CurrentIdentifierFinished() ) + def test_CurrentIdentifierFinished_WhitespaceOnly( self ): + with MockCurrentFiletypes(): + with MockCurrentColumnAndLineContents( 1, '\n' ): + assert_that( base.CurrentIdentifierFinished() ) + with MockCurrentColumnAndLineContents( 3, '\n ' ): + assert_that( base.CurrentIdentifierFinished() ) -def CurrentIdentifierFinished_Unicode_test(): - with MockCurrentFiletypes(): - # CurrentColumn returns a byte offset and character ø is 2 bytes length. - with MockCurrentColumnAndLineContents( 6, 'føo ' ): - assert_that( base.CurrentIdentifierFinished() ) + with MockCurrentColumnAndLineContents( 3, '\t\t\t\t' ): + assert_that( base.CurrentIdentifierFinished() ) - with MockCurrentColumnAndLineContents( 5, 'føo ' ): - assert_that( base.CurrentIdentifierFinished() ) - with MockCurrentColumnAndLineContents( 4, 'føo ' ): - assert_that( not base.CurrentIdentifierFinished() ) + def test_CurrentIdentifierFinished_Unicode( self ): + with MockCurrentFiletypes(): + # CurrentColumn returns a byte offset and character ø is 2 bytes length. + with MockCurrentColumnAndLineContents( 6, 'føo ' ): + assert_that( base.CurrentIdentifierFinished() ) + + with MockCurrentColumnAndLineContents( 5, 'føo ' ): + assert_that( base.CurrentIdentifierFinished() ) + + with MockCurrentColumnAndLineContents( 4, 'føo ' ): + assert_that( not base.CurrentIdentifierFinished() ) - with MockCurrentColumnAndLineContents( 3, 'føo ' ): - assert_that( not base.CurrentIdentifierFinished() ) + with MockCurrentColumnAndLineContents( 3, 'føo ' ): + assert_that( not base.CurrentIdentifierFinished() ) diff --git a/python/ycm/tests/client/base_request_test.py b/python/ycm/tests/client/base_request_test.py index e31f9f69ce..1ad5a06034 100644 --- a/python/ycm/tests/client/base_request_test.py +++ b/python/ycm/tests/client/base_request_test.py @@ -19,22 +19,24 @@ MockVimModule() from hamcrest import assert_that, has_entry +from unittest import TestCase from unittest.mock import patch from ycm.client.base_request import BuildRequestData -@patch( 'ycm.client.base_request.GetCurrentDirectory', - return_value = '/some/dir' ) -def BuildRequestData_AddWorkingDir_test( *args ): - current_buffer = VimBuffer( 'foo' ) - with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): - assert_that( BuildRequestData(), has_entry( 'working_dir', '/some/dir' ) ) +class BaseRequestTest( TestCase ): + @patch( 'ycm.client.base_request.GetCurrentDirectory', + return_value = '/some/dir' ) + def test_BuildRequestData_AddWorkingDir( self, *args ): + current_buffer = VimBuffer( 'foo' ) + with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): + assert_that( BuildRequestData(), has_entry( 'working_dir', '/some/dir' ) ) -@patch( 'ycm.client.base_request.GetCurrentDirectory', - return_value = '/some/dir' ) -def BuildRequestData_AddWorkingDirWithFileName_test( *args ): - current_buffer = VimBuffer( 'foo' ) - with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): - assert_that( BuildRequestData( current_buffer.number ), - has_entry( 'working_dir', '/some/dir' ) ) + @patch( 'ycm.client.base_request.GetCurrentDirectory', + return_value = '/some/dir' ) + def test_BuildRequestData_AddWorkingDirWithFileName( self, *args ): + current_buffer = VimBuffer( 'foo' ) + with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): + assert_that( BuildRequestData( current_buffer.number ), + has_entry( 'working_dir', '/some/dir' ) ) diff --git a/python/ycm/tests/client/command_request_test.py b/python/ycm/tests/client/command_request_test.py index 784c8a8d92..7af6e4852e 100644 --- a/python/ycm/tests/client/command_request_test.py +++ b/python/ycm/tests/client/command_request_test.py @@ -19,8 +19,8 @@ MockVimModule() import json -import pytest from hamcrest import assert_that +from unittest import TestCase from unittest.mock import patch, call from ycm.client.command_request import CommandRequest @@ -87,25 +87,25 @@ def GoToListTest( command, response ): MULTI_FIXIT_SECOND_CHUNKS = MULTI_FIXIT[ 'fixits' ][ 1 ][ 'chunks' ] -class GoToResponse_QuickFix_test: +class GoToResponse_QuickFixTest( TestCase ): """This class tests the generation of QuickFix lists for GoTo responses which return multiple locations, such as the Python completer and JavaScript completer. It mostly proves that we use 1-based indexing for the column number.""" - def setup_method( self ): + def setUp( self ): self._request = CommandRequest( [ 'GoToTest' ] ) - def teardown_method( self ): + def tearDown( self ): self._request = None - def GoTo_EmptyList_test( self ): + def test_GoTo_EmptyList( self ): self._CheckGoToList( [], [] ) - def GoTo_SingleItem_List_test( self ): + def test_GoTo_SingleItem_List( self ): self._CheckGoToList( [ { 'filepath': 'dummy_file', 'line_num': 10, @@ -119,7 +119,7 @@ def GoTo_SingleItem_List_test( self ): } ] ) - def GoTo_MultiItem_List_test( self ): + def test_GoTo_MultiItem_List( self ): self._CheckGoToList( [ { 'filepath': 'dummy_file', 'line_num': 10, @@ -174,15 +174,9 @@ def _CheckGoToList( self, set_fitting_height.assert_called_once_with() -class Response_Detection_test: +class Response_Detection_Test( TestCase ): - @pytest.mark.parametrize( 'command,response', [ - [ 'AnythingYouLike', True ], - [ 'GoToEvenWorks', 10 ], - [ 'FixItWorks', 'String!' ], - [ 'and8434fd andy garbag!', 10.3 ], - ] ) - def BasicResponse_test( self, command, response ): + def test_BasicResponse( self ): def _BasicResponseTest( command, response ): with patch( 'vim.command' ) as vim_command: request = CommandRequest( [ command ] ) @@ -190,13 +184,16 @@ def _BasicResponseTest( command, response ): request.RunPostCommandActionsIfNeeded( 'belowright' ) vim_command.assert_called_with( f"echo '{ response }'" ) - _BasicResponseTest( command, response ) + for command, response in [ + [ 'AnythingYouLike', True ], + [ 'GoToEvenWorks', 10 ], + [ 'FixItWorks', 'String!' ], + [ 'and8434fd andy garbag!', 10.3 ], + ]: + with self.subTest( command = command, response = response ): + _BasicResponseTest( command, response ) - @pytest.mark.parametrize( 'command', [ 'FixIt', - 'Refactor', - 'GoToHell', - 'any_old_garbade!!!21' ] ) - def FixIt_Response_Empty_test( self, command ): + def test_FixIt_Response_Empty( self ): # Ensures we recognise and handle fixit responses which indicate that there # are no fixits available def EmptyFixItTest( command ): @@ -212,27 +209,13 @@ def EmptyFixItTest( command ): 'No fixits found for current line', warning = False ) replace_chunks.assert_not_called() - EmptyFixItTest( command ) + for command in [ 'FixIt', 'Refactor', 'GoToHell', 'any_old_garbade!!!21' ]: + with self.subTest( command = command ): + EmptyFixItTest( command ) - @pytest.mark.parametrize( 'command,response,chunks,selection,silent', [ - [ 'AnythingYouLike', - BASIC_FIXIT, BASIC_FIXIT_CHUNKS, 0, False ], - [ 'GoToEvenWorks', - BASIC_FIXIT, BASIC_FIXIT_CHUNKS, 0, False ], - [ 'FixItWorks', - BASIC_FIXIT, BASIC_FIXIT_CHUNKS, 0, False ], - [ 'and8434fd andy garbag!', - BASIC_FIXIT, BASIC_FIXIT_CHUNKS, 0, False ], - [ 'Format', - BASIC_FIXIT, BASIC_FIXIT_CHUNKS, 0, True ], - [ 'select from multiple 1', - MULTI_FIXIT, MULTI_FIXIT_FIRST_CHUNKS, 0, False ], - [ 'select from multiple 2', - MULTI_FIXIT, MULTI_FIXIT_SECOND_CHUNKS, 1, False ], - ] ) - def FixIt_Response_test( self, command, response, chunks, selection, silent ): + def test_FixIt_Response( self ): # Ensures we recognise and handle fixit responses with some dummy chunk data def FixItTest( command, response, chunks, selection, silent ): with patch( 'ycm.vimsupport.ReplaceChunks' ) as replace_chunks: @@ -247,15 +230,31 @@ def FixItTest( command, response, chunks, selection, silent ): post_vim_message.assert_not_called() - FixItTest( command, response, chunks, selection, silent ) + for command, response, chunks, selection, silent in [ + [ 'AnythingYouLike', + BASIC_FIXIT, BASIC_FIXIT_CHUNKS, 0, False ], + [ 'GoToEvenWorks', + BASIC_FIXIT, BASIC_FIXIT_CHUNKS, 0, False ], + [ 'FixItWorks', + BASIC_FIXIT, BASIC_FIXIT_CHUNKS, 0, False ], + [ 'and8434fd andy garbag!', + BASIC_FIXIT, BASIC_FIXIT_CHUNKS, 0, False ], + [ 'Format', + BASIC_FIXIT, BASIC_FIXIT_CHUNKS, 0, True ], + [ 'select from multiple 1', + MULTI_FIXIT, MULTI_FIXIT_FIRST_CHUNKS, 0, False ], + [ 'select from multiple 2', + MULTI_FIXIT, MULTI_FIXIT_SECOND_CHUNKS, 1, False ], + ]: + with self.subTest( command = command, + response = response, + chunks = chunks, + selection = selection, + silent = silent ): + FixItTest( command, response, chunks, selection, silent ) - @pytest.mark.parametrize( 'command,message', [ - [ '___________', 'This is a message' ], - [ '', 'this is also a message' ], - [ 'GetType', 'std::string' ], - ] ) - def Message_Response_test( self, command, message ): + def test_Message_Response( self ): # Ensures we correctly recognise and handle responses with a message to show # to the user @@ -266,15 +265,16 @@ def MessageTest( command, message ): request.RunPostCommandActionsIfNeeded( 'rightbelow' ) post_vim_message.assert_called_with( message, warning = False ) - MessageTest( command, message ) - - - @pytest.mark.parametrize( 'command,info', [ + for command, message in [ [ '___________', 'This is a message' ], [ '', 'this is also a message' ], - [ 'GetDoc', 'std::string\netc\netc' ], - ] ) - def Detailed_Info_test( self, command, info ): + [ 'GetType', 'std::string' ], + ]: + with self.subTest( command = command, message = message ): + MessageTest( command, message ) + + + def test_Detailed_Info( self ): # Ensures we correctly detect and handle detailed_info responses which are # used to display information in the preview window @@ -285,10 +285,17 @@ def DetailedInfoTest( command, info ): request.RunPostCommandActionsIfNeeded( 'topleft' ) write_to_preview.assert_called_with( info ) - DetailedInfoTest( command, info ) + for command, info in [ + [ '___________', 'This is a message' ], + [ '', 'this is also a message' ], + [ 'GetDoc', 'std::string\netc\netc' ], + ]: + with self.subTest( command = command, info = info ): + DetailedInfoTest( command, info ) - @pytest.mark.parametrize( 'test,command,response', [ + def test_GoTo_Single( self ): + for test, command, response in [ [ GoToTest, 'AnythingYouLike', BASIC_GOTO ], [ GoToTest, 'GoTo', BASIC_GOTO ], [ GoToTest, 'FindAThing', BASIC_GOTO ], @@ -296,6 +303,6 @@ def DetailedInfoTest( command, info ): [ GoToListTest, 'AnythingYouLike', [ BASIC_GOTO ] ], [ GoToListTest, 'GoTo', [] ], [ GoToListTest, 'FixItGoto', [ BASIC_GOTO, BASIC_GOTO ] ], - ] ) - def GoTo_Single_test( self, test, command, response ): - test( command, response ) + ]: + with self.subTest( test = test, command = command, response = response ): + test( command, response ) diff --git a/python/ycm/tests/client/completion_request_test.py b/python/ycm/tests/client/completion_request_test.py index 7375536a32..2ff07fe4e4 100644 --- a/python/ycm/tests/client/completion_request_test.py +++ b/python/ycm/tests/client/completion_request_test.py @@ -17,14 +17,15 @@ import json from hamcrest import assert_that, equal_to -from ycm.tests.conftest import UserOptions +from unittest import TestCase +from ycm.tests import UserOptions from ycm.tests.test_utils import MockVimModule vim_mock = MockVimModule() from ycm.client import completion_request -class ConvertCompletionResponseToVimDatas_test: +class ConvertCompletionResponseToVimDatasTest( TestCase ): """ This class tests the completion_request.ConvertCompletionResponseToVimDatas method """ @@ -44,7 +45,7 @@ def _Check( self, completion_data, expected_vim_data ): raise - def AllFields_test( self ): + def test_AllFields( self ): extra_data = { 'doc_string': 'DOC STRING', } @@ -68,7 +69,7 @@ def AllFields_test( self ): } ) - def OnlyInsertionTextField_test( self ): + def test_OnlyInsertionTextField( self ): self._Check( { 'insertion_text': 'INSERTION TEXT' }, { @@ -84,7 +85,7 @@ def OnlyInsertionTextField_test( self ): } ) - def JustDetailedInfo_test( self ): + def test_JustDetailedInfo( self ): self._Check( { 'insertion_text': 'INSERTION TEXT', 'menu_text': 'MENU TEXT', @@ -104,7 +105,7 @@ def JustDetailedInfo_test( self ): } ) - def JustDocString_test( self ): + def test_JustDocString( self ): extra_data = { 'doc_string': 'DOC STRING', } @@ -127,7 +128,7 @@ def JustDocString_test( self ): } ) - def ExtraInfoNoDocString_test( self ): + def test_ExtraInfoNoDocString( self ): self._Check( { 'insertion_text': 'INSERTION TEXT', 'menu_text': 'MENU TEXT', @@ -148,7 +149,7 @@ def ExtraInfoNoDocString_test( self ): } ) - def NullCharactersInExtraInfoAndDocString_test( self ): + def test_NullCharactersInExtraInfoAndDocString( self ): extra_data = { 'doc_string': 'DOC\x00STRING' } @@ -172,7 +173,7 @@ def NullCharactersInExtraInfoAndDocString_test( self ): } ) - def ExtraInfoNoDocStringWithDetailedInfo_test( self ): + def test_ExtraInfoNoDocStringWithDetailedInfo( self ): self._Check( { 'insertion_text': 'INSERTION TEXT', 'menu_text': 'MENU TEXT', @@ -194,7 +195,7 @@ def ExtraInfoNoDocStringWithDetailedInfo_test( self ): } ) - def EmptyInsertionText_test( self ): + def test_EmptyInsertionText( self ): extra_data = { 'doc_string': 'DOC STRING', } @@ -218,7 +219,7 @@ def EmptyInsertionText_test( self ): } ) - def TruncateForPopup_test( self, *args ): + def test_TruncateForPopup( self, *args ): with UserOptions( { '&columns': 60, '&completeopt': b'popup,menuone' } ): extra_data = { 'doc_string': 'DOC STRING', @@ -244,7 +245,7 @@ def TruncateForPopup_test( self, *args ): } ) - def OnlyTruncateForPopupIfNecessary_test( self, *args ): + def test_OnlyTruncateForPopupIfNecessary( self, *args ): with UserOptions( { '&columns': 60, '&completeopt': b'popup,menuone' } ): extra_data = { 'doc_string': 'DOC STRING', @@ -269,7 +270,7 @@ def OnlyTruncateForPopupIfNecessary_test( self, *args ): } ) - def DontTruncateIfNotPopup_test( self, *args ): + def test_DontTruncateIfNotPopup( self, *args ): with UserOptions( { '&columns': 60, '&completeopt': b'preview,menuone' } ): extra_data = { 'doc_string': 'DOC STRING', @@ -294,7 +295,7 @@ def DontTruncateIfNotPopup_test( self, *args ): } ) - def TruncateForPopupWithoutDuplication_test( self, *args ): + def test_TruncateForPopupWithoutDuplication( self, *args ): with UserOptions( { '&columns': 60, '&completeopt': b'popup,menuone' } ): extra_data = { 'doc_string': 'DOC STRING', diff --git a/python/ycm/tests/client/debug_info_request_test.py b/python/ycm/tests/client/debug_info_request_test.py index 93f7ac393b..9c3aa7b8e0 100644 --- a/python/ycm/tests/client/debug_info_request_test.py +++ b/python/ycm/tests/client/debug_info_request_test.py @@ -17,6 +17,7 @@ from copy import deepcopy from hamcrest import assert_that, contains_string, equal_to +from unittest import TestCase from ycm.client.debug_info_request import FormatDebugInfoResponse @@ -66,111 +67,113 @@ } -def FormatDebugInfoResponse_NoResponse_test(): - assert_that( - FormatDebugInfoResponse( None ), - equal_to( 'Server errored, no debug info from server\n' ) - ) +class DebugInfoRequestTest( TestCase ): + def test_FormatDebugInfoResponse_NoResponse( self ): + assert_that( + FormatDebugInfoResponse( None ), + equal_to( 'Server errored, no debug info from server\n' ) + ) -def FormatDebugInfoResponse_NoExtraConf_test(): - response = deepcopy( GENERIC_RESPONSE ) - response[ 'extra_conf' ].update( { - 'is_loaded': False, - 'path': None - } ) - assert_that( - FormatDebugInfoResponse( response ), - contains_string( - 'No extra configuration file found\n' + def test_FormatDebugInfoResponse_NoExtraConf( self ): + response = deepcopy( GENERIC_RESPONSE ) + response[ 'extra_conf' ].update( { + 'is_loaded': False, + 'path': None + } ) + assert_that( + FormatDebugInfoResponse( response ), + contains_string( + 'No extra configuration file found\n' + ) ) - ) -def FormatDebugInfoResponse_ExtraConfFoundButNotLoaded_test(): - response = deepcopy( GENERIC_RESPONSE ) - response[ 'extra_conf' ].update( { - 'is_loaded': False, - 'path': '/path/to/extra/conf' - } ) - assert_that( - FormatDebugInfoResponse( response ), - contains_string( - 'Extra configuration file found but not loaded\n' - 'Extra configuration path: /path/to/extra/conf\n' + def test_FormatDebugInfoResponse_ExtraConfFoundButNotLoaded( self ): + response = deepcopy( GENERIC_RESPONSE ) + response[ 'extra_conf' ].update( { + 'is_loaded': False, + 'path': '/path/to/extra/conf' + } ) + assert_that( + FormatDebugInfoResponse( response ), + contains_string( + 'Extra configuration file found but not loaded\n' + 'Extra configuration path: /path/to/extra/conf\n' + ) ) - ) -def FormatDebugInfoResponse_ExtraConfFoundAndLoaded_test(): - response = deepcopy( GENERIC_RESPONSE ) - response[ 'extra_conf' ].update( { - 'is_loaded': True, - 'path': '/path/to/extra/conf' - } ) - assert_that( - FormatDebugInfoResponse( response ), - contains_string( - 'Extra configuration file found and loaded\n' - 'Extra configuration path: /path/to/extra/conf\n' + def test_FormatDebugInfoResponse_ExtraConfFoundAndLoaded( self ): + response = deepcopy( GENERIC_RESPONSE ) + response[ 'extra_conf' ].update( { + 'is_loaded': True, + 'path': '/path/to/extra/conf' + } ) + assert_that( + FormatDebugInfoResponse( response ), + contains_string( + 'Extra configuration file found and loaded\n' + 'Extra configuration path: /path/to/extra/conf\n' + ) ) - ) - - -def FormatDebugInfoResponse_Completer_ServerRunningWithHost_test(): - response = deepcopy( GENERIC_RESPONSE ) - assert_that( - FormatDebugInfoResponse( response ), - contains_string( - 'Completer name completer debug information:\n' - ' Server name running at: http://127.0.0.1:1234\n' - ' Server name process ID: 12345\n' - ' Server name executable: /path/to/executable\n' - ' Server name logfiles:\n' - ' /path/to/stdout/logfile\n' - ' /path/to/stderr/logfile\n' - ' Server name key: value\n' - ' Key: value\n' + + + def test_FormatDebugInfoResponse_Completer_ServerRunningWithHost( self ): + response = deepcopy( GENERIC_RESPONSE ) + assert_that( + FormatDebugInfoResponse( response ), + contains_string( + 'Completer name completer debug information:\n' + ' Server name running at: http://127.0.0.1:1234\n' + ' Server name process ID: 12345\n' + ' Server name executable: /path/to/executable\n' + ' Server name logfiles:\n' + ' /path/to/stdout/logfile\n' + ' /path/to/stderr/logfile\n' + ' Server name key: value\n' + ' Key: value\n' + ) ) - ) - - -def FormatDebugInfoResponse_Completer_ServerRunningWithoutHost_test(): - response = deepcopy( GENERIC_RESPONSE ) - response[ 'completer' ][ 'servers' ][ 0 ].update( { - 'address': None, - 'port': None - } ) - assert_that( - FormatDebugInfoResponse( response ), - contains_string( - 'Completer name completer debug information:\n' - ' Server name running\n' - ' Server name process ID: 12345\n' - ' Server name executable: /path/to/executable\n' - ' Server name logfiles:\n' - ' /path/to/stdout/logfile\n' - ' /path/to/stderr/logfile\n' - ' Server name key: value\n' - ' Key: value\n' + + + def test_FormatDebugInfoResponse_Completer_ServerRunningWithoutHost( self ): + response = deepcopy( GENERIC_RESPONSE ) + response[ 'completer' ][ 'servers' ][ 0 ].update( { + 'address': None, + 'port': None + } ) + assert_that( + FormatDebugInfoResponse( response ), + contains_string( + 'Completer name completer debug information:\n' + ' Server name running\n' + ' Server name process ID: 12345\n' + ' Server name executable: /path/to/executable\n' + ' Server name logfiles:\n' + ' /path/to/stdout/logfile\n' + ' /path/to/stderr/logfile\n' + ' Server name key: value\n' + ' Key: value\n' + ) ) - ) - - -def FormatDebugInfoResponse_Completer_ServerNotRunningWithNoLogfiles_test(): - response = deepcopy( GENERIC_RESPONSE ) - response[ 'completer' ][ 'servers' ][ 0 ].update( { - 'is_running': False, - 'logfiles': [] - } ) - assert_that( - FormatDebugInfoResponse( response ), - contains_string( - 'Completer name completer debug information:\n' - ' Server name not running\n' - ' Server name executable: /path/to/executable\n' - ' No logfiles available\n' - ' Server name key: value\n' - ' Key: value\n' + + + def test_FormatDebugInfoResponse_Completer_ServerNotRunningWithNoLogfiles( + self ): + response = deepcopy( GENERIC_RESPONSE ) + response[ 'completer' ][ 'servers' ][ 0 ].update( { + 'is_running': False, + 'logfiles': [] + } ) + assert_that( + FormatDebugInfoResponse( response ), + contains_string( + 'Completer name completer debug information:\n' + ' Server name not running\n' + ' Server name executable: /path/to/executable\n' + ' No logfiles available\n' + ' Server name key: value\n' + ' Key: value\n' + ) ) - ) diff --git a/python/ycm/tests/client/messages_request_test.py b/python/ycm/tests/client/messages_request_test.py index aa5931deb2..1e1a10e900 100644 --- a/python/ycm/tests/client/messages_request_test.py +++ b/python/ycm/tests/client/messages_request_test.py @@ -19,116 +19,122 @@ MockVimModule() from hamcrest import assert_that, equal_to +from unittest import TestCase from unittest.mock import patch, call from ycm.client.messages_request import _HandlePollResponse from ycm.tests.test_utils import ExtendedMock -def HandlePollResponse_NoMessages_test(): - assert_that( _HandlePollResponse( True, None ), equal_to( True ) ) - - # Other non-False responses mean the same thing - assert_that( _HandlePollResponse( '', None ), equal_to( True ) ) - assert_that( _HandlePollResponse( 1, None ), equal_to( True ) ) - assert_that( _HandlePollResponse( {}, None ), equal_to( True ) ) - - -def HandlePollResponse_PollingNotSupported_test(): - assert_that( _HandlePollResponse( False, None ), equal_to( False ) ) - - # 0 is not False - assert_that( _HandlePollResponse( 0, None ), equal_to( True ) ) - - -@patch( 'ycm.client.messages_request.PostVimMessage', - new_callable = ExtendedMock ) -def HandlePollResponse_SingleMessage_test( post_vim_message ): - assert_that( _HandlePollResponse( [ { 'message': 'this is a message' } ] , - None ), - equal_to( True ) ) - - post_vim_message.assert_has_exact_calls( [ - call( 'this is a message', warning=False, truncate=True ) - ] ) - - -@patch( 'ycm.client.messages_request.PostVimMessage', - new_callable = ExtendedMock ) -def HandlePollResponse_MultipleMessages_test( post_vim_message ): - assert_that( _HandlePollResponse( [ { 'message': 'this is a message' }, - { 'message': 'this is another one' } ] , - None ), - equal_to( True ) ) - - post_vim_message.assert_has_exact_calls( [ - call( 'this is a message', warning=False, truncate=True ), - call( 'this is another one', warning=False, truncate=True ) - ] ) - - -def HandlePollResponse_SingleDiagnostic_test(): - diagnostics_handler = ExtendedMock() - messages = [ - { 'filepath': 'foo', 'diagnostics': [ 'PLACEHOLDER' ] }, - ] - assert_that( _HandlePollResponse( messages, diagnostics_handler ), - equal_to( True ) ) - diagnostics_handler.UpdateWithNewDiagnosticsForFile.assert_has_exact_calls( [ - call( 'foo', [ 'PLACEHOLDER' ] ) - ] ) - - -def HandlePollResponse_MultipleDiagnostics_test(): - diagnostics_handler = ExtendedMock() - messages = [ - { 'filepath': 'foo', 'diagnostics': [ 'PLACEHOLDER1' ] }, - { 'filepath': 'bar', 'diagnostics': [ 'PLACEHOLDER2' ] }, - { 'filepath': 'baz', 'diagnostics': [ 'PLACEHOLDER3' ] }, - { 'filepath': 'foo', 'diagnostics': [ 'PLACEHOLDER4' ] }, - ] - assert_that( _HandlePollResponse( messages, diagnostics_handler ), - equal_to( True ) ) - diagnostics_handler.UpdateWithNewDiagnosticsForFile.assert_has_exact_calls( [ - call( 'foo', [ 'PLACEHOLDER1' ] ), - call( 'bar', [ 'PLACEHOLDER2' ] ), - call( 'baz', [ 'PLACEHOLDER3' ] ), - call( 'foo', [ 'PLACEHOLDER4' ] ) - ] ) - - -@patch( 'ycm.client.messages_request.PostVimMessage', - new_callable = ExtendedMock ) -def HandlePollResponse_MultipleMessagesAndDiagnostics_test( post_vim_message ): - diagnostics_handler = ExtendedMock() - messages = [ - { 'filepath': 'foo', 'diagnostics': [ 'PLACEHOLDER1' ] }, - { 'message': 'On the first day of Christmas, my VimScript gave to me' }, - { 'filepath': 'bar', 'diagnostics': [ 'PLACEHOLDER2' ] }, - { 'message': 'A test file in a Command-T' }, - { 'filepath': 'baz', 'diagnostics': [ 'PLACEHOLDER3' ] }, - { 'message': 'On the second day of Christmas, my VimScript gave to me' }, - { 'filepath': 'foo', 'diagnostics': [ 'PLACEHOLDER4' ] }, - { 'message': 'Two popup menus, and a test file in a Command-T' }, - ] - assert_that( _HandlePollResponse( messages, diagnostics_handler ), - equal_to( True ) ) - diagnostics_handler.UpdateWithNewDiagnosticsForFile.assert_has_exact_calls( [ - call( 'foo', [ 'PLACEHOLDER1' ] ), - call( 'bar', [ 'PLACEHOLDER2' ] ), - call( 'baz', [ 'PLACEHOLDER3' ] ), - call( 'foo', [ 'PLACEHOLDER4' ] ) - ] ) - - post_vim_message.assert_has_exact_calls( [ - call( 'On the first day of Christmas, my VimScript gave to me', - warning=False, - truncate=True ), - call( 'A test file in a Command-T', warning=False, truncate=True ), - call( 'On the second day of Christmas, my VimScript gave to me', - warning=False, - truncate=True ), - call( 'Two popup menus, and a test file in a Command-T', - warning=False, - truncate=True ), - ] ) +class MessagesRequestTest( TestCase ): + def test_HandlePollResponse_NoMessages( self ): + assert_that( _HandlePollResponse( True, None ), equal_to( True ) ) + + # Other non-False responses mean the same thing + assert_that( _HandlePollResponse( '', None ), equal_to( True ) ) + assert_that( _HandlePollResponse( 1, None ), equal_to( True ) ) + assert_that( _HandlePollResponse( {}, None ), equal_to( True ) ) + + + def test_HandlePollResponse_PollingNotSupported( self ): + assert_that( _HandlePollResponse( False, None ), equal_to( False ) ) + + # 0 is not False + assert_that( _HandlePollResponse( 0, None ), equal_to( True ) ) + + + @patch( 'ycm.client.messages_request.PostVimMessage', + new_callable = ExtendedMock ) + def test_HandlePollResponse_SingleMessage( self, post_vim_message ): + assert_that( _HandlePollResponse( [ { 'message': 'this is a message' } ] , + None ), + equal_to( True ) ) + + post_vim_message.assert_has_exact_calls( [ + call( 'this is a message', warning=False, truncate=True ) + ] ) + + + @patch( 'ycm.client.messages_request.PostVimMessage', + new_callable = ExtendedMock ) + def test_HandlePollResponse_MultipleMessages( self, post_vim_message ): + assert_that( _HandlePollResponse( [ { 'message': 'this is a message' }, + { 'message': 'this is another one' } ] , + None ), + equal_to( True ) ) + + post_vim_message.assert_has_exact_calls( [ + call( 'this is a message', warning=False, truncate=True ), + call( 'this is another one', warning=False, truncate=True ) + ] ) + + + def test_HandlePollResponse_SingleDiagnostic( self ): + diagnostics_handler = ExtendedMock() + messages = [ + { 'filepath': 'foo', 'diagnostics': [ 'PLACEHOLDER' ] }, + ] + assert_that( _HandlePollResponse( messages, diagnostics_handler ), + equal_to( True ) ) + diagnostics_handler.UpdateWithNewDiagnosticsForFile.assert_has_exact_calls( + [ + call( 'foo', [ 'PLACEHOLDER' ] ) + ] ) + + + def test_HandlePollResponse_MultipleDiagnostics( self ): + diagnostics_handler = ExtendedMock() + messages = [ + { 'filepath': 'foo', 'diagnostics': [ 'PLACEHOLDER1' ] }, + { 'filepath': 'bar', 'diagnostics': [ 'PLACEHOLDER2' ] }, + { 'filepath': 'baz', 'diagnostics': [ 'PLACEHOLDER3' ] }, + { 'filepath': 'foo', 'diagnostics': [ 'PLACEHOLDER4' ] }, + ] + assert_that( _HandlePollResponse( messages, diagnostics_handler ), + equal_to( True ) ) + diagnostics_handler.UpdateWithNewDiagnosticsForFile.assert_has_exact_calls( + [ + call( 'foo', [ 'PLACEHOLDER1' ] ), + call( 'bar', [ 'PLACEHOLDER2' ] ), + call( 'baz', [ 'PLACEHOLDER3' ] ), + call( 'foo', [ 'PLACEHOLDER4' ] ) + ] ) + + + @patch( 'ycm.client.messages_request.PostVimMessage', + new_callable = ExtendedMock ) + def test_HandlePollResponse_MultipleMessagesAndDiagnostics( + self, post_vim_message ): + diagnostics_handler = ExtendedMock() + messages = [ + { 'filepath': 'foo', 'diagnostics': [ 'PLACEHOLDER1' ] }, + { 'message': 'On the first day of Christmas, my VimScript gave to me' }, + { 'filepath': 'bar', 'diagnostics': [ 'PLACEHOLDER2' ] }, + { 'message': 'A test file in a Command-T' }, + { 'filepath': 'baz', 'diagnostics': [ 'PLACEHOLDER3' ] }, + { 'message': 'On the second day of Christmas, my VimScript gave to me' }, + { 'filepath': 'foo', 'diagnostics': [ 'PLACEHOLDER4' ] }, + { 'message': 'Two popup menus, and a test file in a Command-T' }, + ] + assert_that( _HandlePollResponse( messages, diagnostics_handler ), + equal_to( True ) ) + diagnostics_handler.UpdateWithNewDiagnosticsForFile.assert_has_exact_calls( + [ + call( 'foo', [ 'PLACEHOLDER1' ] ), + call( 'bar', [ 'PLACEHOLDER2' ] ), + call( 'baz', [ 'PLACEHOLDER3' ] ), + call( 'foo', [ 'PLACEHOLDER4' ] ) + ] ) + + post_vim_message.assert_has_exact_calls( [ + call( 'On the first day of Christmas, my VimScript gave to me', + warning=False, + truncate=True ), + call( 'A test file in a Command-T', warning=False, truncate=True ), + call( 'On the second day of Christmas, my VimScript gave to me', + warning=False, + truncate=True ), + call( 'Two popup menus, and a test file in a Command-T', + warning=False, + truncate=True ), + ] ) diff --git a/python/ycm/tests/client/omni_completion_request_test.py b/python/ycm/tests/client/omni_completion_request_test.py index b9f611ab90..a78f2da6ae 100644 --- a/python/ycm/tests/client/omni_completion_request_test.py +++ b/python/ycm/tests/client/omni_completion_request_test.py @@ -15,6 +15,7 @@ # You should have received a copy of the GNU General Public License # along with YouCompleteMe. If not, see . +from unittest import TestCase from unittest.mock import MagicMock from hamcrest import assert_that, has_entries @@ -36,19 +37,20 @@ def BuildOmnicompletionRequest( results, start_column = 1 ): return request -def Done_AlwaysTrue_test(): - request = BuildOmnicompletionRequest( [] ) +class OmniCompletionRequestTest( TestCase ): + def test_Done_AlwaysTrue( self ): + request = BuildOmnicompletionRequest( [] ) - assert_that( request.Done() ) + assert_that( request.Done() ) -def Response_FromOmniCompleter_test(): - results = [ { "word": "test" } ] - request = BuildOmnicompletionRequest( results ) + def test_Response_FromOmniCompleter( self ): + results = [ { "word": "test" } ] + request = BuildOmnicompletionRequest( results ) - assert_that( request.Response(), has_entries( { - 'line': 1, - 'column': 1, - 'completion_start_column': 1, - 'completions': results - } ) ) + assert_that( request.Response(), has_entries( { + 'line': 1, + 'column': 1, + 'completion_start_column': 1, + 'completions': results + } ) ) diff --git a/python/ycm/tests/command_test.py b/python/ycm/tests/command_test.py index fbb9d4644d..b938e94e6c 100644 --- a/python/ycm/tests/command_test.py +++ b/python/ycm/tests/command_test.py @@ -20,142 +20,145 @@ from hamcrest import assert_that, contains_exactly, has_entries from unittest.mock import patch +from unittest import TestCase from ycm.tests import YouCompleteMeInstance -@YouCompleteMeInstance( { 'g:ycm_extra_conf_vim_data': [ 'tempname()' ] } ) -def SendCommandRequest_ExtraConfVimData_Works_test( ycm ): - current_buffer = VimBuffer( 'buffer' ) - with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): - with patch( 'ycm.youcompleteme.SendCommandRequest' ) as send_request: - ycm.SendCommandRequest( [ 'GoTo' ], 'aboveleft', False, 1, 1 ) - assert_that( - # Positional arguments passed to SendCommandRequest. - send_request.call_args[ 0 ], - contains_exactly( - contains_exactly( 'GoTo' ), - 'aboveleft', - 'same-buffer', - has_entries( { - 'options': has_entries( { - 'tab_size': 2, - 'insert_spaces': True, - } ), - 'extra_conf_data': has_entries( { - 'tempname()': '_TEMP_FILE_' - } ), - } ) +class CommandTest( TestCase ): + @YouCompleteMeInstance( { 'g:ycm_extra_conf_vim_data': [ 'tempname()' ] } ) + def test_SendCommandRequest_ExtraConfVimData_Works( self, ycm ): + current_buffer = VimBuffer( 'buffer' ) + with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): + with patch( 'ycm.youcompleteme.SendCommandRequest' ) as send_request: + ycm.SendCommandRequest( [ 'GoTo' ], 'aboveleft', False, 1, 1 ) + assert_that( + # Positional arguments passed to SendCommandRequest. + send_request.call_args[ 0 ], + contains_exactly( + contains_exactly( 'GoTo' ), + 'aboveleft', + 'same-buffer', + has_entries( { + 'options': has_entries( { + 'tab_size': 2, + 'insert_spaces': True, + } ), + 'extra_conf_data': has_entries( { + 'tempname()': '_TEMP_FILE_' + } ), + } ) + ) + ) + + + @YouCompleteMeInstance( { + 'g:ycm_extra_conf_vim_data': [ 'undefined_value' ] } ) + def test_SendCommandRequest_ExtraConfData_UndefinedValue( self, ycm ): + current_buffer = VimBuffer( 'buffer' ) + with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): + with patch( 'ycm.youcompleteme.SendCommandRequest' ) as send_request: + ycm.SendCommandRequest( [ 'GoTo' ], 'belowright', False, 1, 1 ) + assert_that( + # Positional arguments passed to SendCommandRequest. + send_request.call_args[ 0 ], + contains_exactly( + contains_exactly( 'GoTo' ), + 'belowright', + 'same-buffer', + has_entries( { + 'options': has_entries( { + 'tab_size': 2, + 'insert_spaces': True, + } ) + } ) + ) ) - ) -@YouCompleteMeInstance( { 'g:ycm_extra_conf_vim_data': [ 'undefined_value' ] } ) -def SendCommandRequest_ExtraConfData_UndefinedValue_test( ycm ): - current_buffer = VimBuffer( 'buffer' ) - with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): - with patch( 'ycm.youcompleteme.SendCommandRequest' ) as send_request: - ycm.SendCommandRequest( [ 'GoTo' ], 'belowright', False, 1, 1 ) - assert_that( - # Positional arguments passed to SendCommandRequest. - send_request.call_args[ 0 ], - contains_exactly( - contains_exactly( 'GoTo' ), - 'belowright', + @YouCompleteMeInstance() + def test_SendCommandRequest_BuildRange_NoVisualMarks( self, ycm, *args ): + current_buffer = VimBuffer( 'buffer', contents = [ 'first line', + 'second line' ] ) + with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): + with patch( 'ycm.youcompleteme.SendCommandRequest' ) as send_request: + ycm.SendCommandRequest( [ 'GoTo' ], '', True, 1, 2 ) + send_request.assert_called_once_with( + [ 'GoTo' ], + '', 'same-buffer', - has_entries( { - 'options': has_entries( { + { + 'options': { 'tab_size': 2, - 'insert_spaces': True, - } ) - } ) + 'insert_spaces': True + }, + 'range': { + 'start': { + 'line_num': 1, + 'column_num': 1 + }, + 'end': { + 'line_num': 2, + 'column_num': 12 + } + } + } ) - ) -@YouCompleteMeInstance() -def SendCommandRequest_BuildRange_NoVisualMarks_test( ycm, *args ): - current_buffer = VimBuffer( 'buffer', contents = [ 'first line', - 'second line' ] ) - with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): - with patch( 'ycm.youcompleteme.SendCommandRequest' ) as send_request: - ycm.SendCommandRequest( [ 'GoTo' ], '', True, 1, 2 ) - send_request.assert_called_once_with( - [ 'GoTo' ], - '', - 'same-buffer', - { - 'options': { - 'tab_size': 2, - 'insert_spaces': True - }, - 'range': { - 'start': { - 'line_num': 1, - 'column_num': 1 + @YouCompleteMeInstance() + def test_SendCommandRequest_BuildRange_VisualMarks( self, ycm, *args ): + current_buffer = VimBuffer( 'buffer', + contents = [ 'first line', + 'second line' ], + visual_start = [ 1, 4 ], + visual_end = [ 2, 8 ] ) + with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): + with patch( 'ycm.youcompleteme.SendCommandRequest' ) as send_request: + ycm.SendCommandRequest( [ 'GoTo' ], 'tab', True, 1, 2 ) + send_request.assert_called_once_with( + [ 'GoTo' ], + 'tab', + 'same-buffer', + { + 'options': { + 'tab_size': 2, + 'insert_spaces': True }, - 'end': { - 'line_num': 2, - 'column_num': 12 + 'range': { + 'start': { + 'line_num': 1, + 'column_num': 5 + }, + 'end': { + 'line_num': 2, + 'column_num': 9 + } } } - } - ) + ) -@YouCompleteMeInstance() -def SendCommandRequest_BuildRange_VisualMarks_test( ycm, *args ): - current_buffer = VimBuffer( 'buffer', - contents = [ 'first line', - 'second line' ], - visual_start = [ 1, 4 ], - visual_end = [ 2, 8 ] ) - with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): - with patch( 'ycm.youcompleteme.SendCommandRequest' ) as send_request: - ycm.SendCommandRequest( [ 'GoTo' ], 'tab', True, 1, 2 ) - send_request.assert_called_once_with( + @YouCompleteMeInstance() + def test_SendCommandRequest_IgnoreFileTypeOption( self, ycm, *args ): + current_buffer = VimBuffer( 'buffer' ) + with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): + expected_args = ( [ 'GoTo' ], - 'tab', + '', 'same-buffer', { 'options': { 'tab_size': 2, 'insert_spaces': True }, - 'range': { - 'start': { - 'line_num': 1, - 'column_num': 5 - }, - 'end': { - 'line_num': 2, - 'column_num': 9 - } - } } ) + with patch( 'ycm.youcompleteme.SendCommandRequest' ) as send_request: + ycm.SendCommandRequest( [ 'ft=ycm:ident', 'GoTo' ], '', False, 1, 1 ) + send_request.assert_called_once_with( *expected_args ) -@YouCompleteMeInstance() -def SendCommandRequest_IgnoreFileTypeOption_test( ycm, *args ): - current_buffer = VimBuffer( 'buffer' ) - with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): - expected_args = ( - [ 'GoTo' ], - '', - 'same-buffer', - { - 'options': { - 'tab_size': 2, - 'insert_spaces': True - }, - } - ) - - with patch( 'ycm.youcompleteme.SendCommandRequest' ) as send_request: - ycm.SendCommandRequest( [ 'ft=ycm:ident', 'GoTo' ], '', False, 1, 1 ) - send_request.assert_called_once_with( *expected_args ) - - with patch( 'ycm.youcompleteme.SendCommandRequest' ) as send_request: - ycm.SendCommandRequest( [ 'GoTo', 'ft=python' ], '', False, 1, 1 ) - send_request.assert_called_once_with( *expected_args ) + with patch( 'ycm.youcompleteme.SendCommandRequest' ) as send_request: + ycm.SendCommandRequest( [ 'GoTo', 'ft=python' ], '', False, 1, 1 ) + send_request.assert_called_once_with( *expected_args ) diff --git a/python/ycm/tests/completion_test.py b/python/ycm/tests/completion_test.py index 8e45bea398..b5c0565932 100644 --- a/python/ycm/tests/completion_test.py +++ b/python/ycm/tests/completion_test.py @@ -25,6 +25,7 @@ empty, equal_to, has_entries ) +from unittest import TestCase from unittest.mock import call, MagicMock, patch from ycm.tests import PathToTestFile, YouCompleteMeInstance @@ -68,410 +69,416 @@ def MockResolveRequest( response_method ): yield -@YouCompleteMeInstance() -def SendCompletionRequest_UnicodeWorkingDirectory_test( ycm ): - unicode_dir = PathToTestFile( 'uni¢od€' ) - current_buffer = VimBuffer( PathToTestFile( 'uni¢𐍈d€', 'current_buffer' ) ) +class CompletionTest( TestCase ): + @YouCompleteMeInstance() + def test_SendCompletionRequest_UnicodeWorkingDirectory( self, ycm ): + unicode_dir = PathToTestFile( 'uni¢od€' ) + current_buffer = VimBuffer( PathToTestFile( 'uni¢𐍈d€', 'current_buffer' ) ) + + def ServerResponse( *args ): + return { 'completions': [], 'completion_start_column': 1 } + + with CurrentWorkingDirectory( unicode_dir ): + with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): + with MockCompletionRequest( ServerResponse ): + ycm.SendCompletionRequest() + assert_that( ycm.CompletionRequestReady() ) + assert_that( + ycm.GetCompletionResponse(), + has_entries( { + 'completions': empty(), + 'completion_start_column': 1 + } ) + ) + + + @YouCompleteMeInstance() + @patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) + def test_SendCompletionRequest_ResponseContainingError( + self, ycm, post_vim_message ): + current_buffer = VimBuffer( 'buffer' ) + + def ServerResponse( *args ): + return { + 'completions': [ { + 'insertion_text': 'insertion_text', + 'menu_text': 'menu_text', + 'extra_menu_info': 'extra_menu_info', + 'detailed_info': 'detailed_info', + 'kind': 'kind', + 'extra_data': { + 'doc_string': 'doc_string' + } + } ], + 'completion_start_column': 3, + 'errors': [ { + 'exception': { + 'TYPE': 'Exception' + }, + 'message': 'message', + 'traceback': 'traceback' + } ] + } - def ServerResponse( *args ): - return { 'completions': [], 'completion_start_column': 1 } - - with CurrentWorkingDirectory( unicode_dir ): with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): with MockCompletionRequest( ServerResponse ): ycm.SendCompletionRequest() assert_that( ycm.CompletionRequestReady() ) + response = ycm.GetCompletionResponse() + post_vim_message.assert_has_exact_calls( [ + call( 'Exception: message', truncate = True ) + ] ) assert_that( - ycm.GetCompletionResponse(), + response, + has_entries( { + 'completions': contains_exactly( has_entries( { + 'word': 'insertion_text', + 'abbr': 'menu_text', + 'menu': 'extra_menu_info', + 'info': 'detailed_info\ndoc_string', + 'kind': 'k', + 'dup': 1, + 'empty': 1 + } ) ), + 'completion_start_column': 3 + } ) + ) + + + @YouCompleteMeInstance() + @patch( 'ycm.client.base_request._logger', autospec = True ) + @patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) + def test_SendCompletionRequest_ErrorFromServer( self, + ycm, + post_vim_message, + logger ): + current_buffer = VimBuffer( 'buffer' ) + with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): + with MockCompletionRequest( ServerError( 'Server error' ) ): + ycm.SendCompletionRequest() + assert_that( ycm.CompletionRequestReady() ) + response = ycm.GetCompletionResponse() + logger.exception.assert_called_with( 'Error while handling server ' + 'response' ) + post_vim_message.assert_has_exact_calls( [ + call( 'Server error', truncate = True ) + ] ) + assert_that( + response, has_entries( { 'completions': empty(), - 'completion_start_column': 1 + 'completion_start_column': -1 } ) ) -@YouCompleteMeInstance() -@patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) -def SendCompletionRequest_ResponseContainingError_test( post_vim_message, ycm ): - current_buffer = VimBuffer( 'buffer' ) - - def ServerResponse( *args ): - return { - 'completions': [ { - 'insertion_text': 'insertion_text', - 'menu_text': 'menu_text', - 'extra_menu_info': 'extra_menu_info', - 'detailed_info': 'detailed_info', - 'kind': 'kind', - 'extra_data': { - 'doc_string': 'doc_string' - } - } ], - 'completion_start_column': 3, - 'errors': [ { - 'exception': { - 'TYPE': 'Exception' + + @YouCompleteMeInstance() + @patch( 'ycm.client.base_request._logger', autospec = True ) + @patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) + def test_ResolveCompletionRequest_Resolves( self, + ycm, + post_vim_message, + logger ): + + def CompletionResponse( *args ): + return { + 'completions': [ { + 'insertion_text': 'insertion_text', + 'menu_text': 'menu_text', + 'extra_menu_info': 'extra_menu_info', + 'detailed_info': 'detailed_info', + 'kind': 'kind', + 'extra_data': { + 'doc_string': 'doc_string', + 'resolve': 10 + } + } ], + 'completion_start_column': 3, + 'errors': [] + } + + def ResolveResponse( *args ): + return { + 'completion': { + 'insertion_text': 'insertion_text', + 'menu_text': 'menu_text', + 'extra_menu_info': 'extra_menu_info', + 'detailed_info': 'detailed_info', + 'kind': 'kind', + 'extra_data': { + 'doc_string': 'doc_string with more info' + } }, - 'message': 'message', - 'traceback': 'traceback' - } ] - } - - with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): - with MockCompletionRequest( ServerResponse ): - ycm.SendCompletionRequest() - assert_that( ycm.CompletionRequestReady() ) - response = ycm.GetCompletionResponse() - post_vim_message.assert_has_exact_calls( [ - call( 'Exception: message', truncate = True ) - ] ) - assert_that( - response, - has_entries( { - 'completions': contains_exactly( has_entries( { - 'word': 'insertion_text', - 'abbr': 'menu_text', - 'menu': 'extra_menu_info', - 'info': 'detailed_info\ndoc_string', - 'kind': 'k', - 'dup': 1, - 'empty': 1 - } ) ), - 'completion_start_column': 3 - } ) - ) - - -@YouCompleteMeInstance() -@patch( 'ycm.client.base_request._logger', autospec = True ) -@patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) -def SendCompletionRequest_ErrorFromServer_test( post_vim_message, - logger, - ycm ): - current_buffer = VimBuffer( 'buffer' ) - with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): - with MockCompletionRequest( ServerError( 'Server error' ) ): - ycm.SendCompletionRequest() - assert_that( ycm.CompletionRequestReady() ) - response = ycm.GetCompletionResponse() - logger.exception.assert_called_with( 'Error while handling server ' - 'response' ) - post_vim_message.assert_has_exact_calls( [ - call( 'Server error', truncate = True ) - ] ) - assert_that( - response, - has_entries( { - 'completions': empty(), - 'completion_start_column': -1 - } ) - ) - - - -@YouCompleteMeInstance() -@patch( 'ycm.client.base_request._logger', autospec = True ) -@patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) -def ResolveCompletionRequest_Resolves_test( post_vim_message, - logger, - ycm ): - - def CompletionResponse( *args ): - return { - 'completions': [ { - 'insertion_text': 'insertion_text', - 'menu_text': 'menu_text', - 'extra_menu_info': 'extra_menu_info', - 'detailed_info': 'detailed_info', - 'kind': 'kind', - 'extra_data': { - 'doc_string': 'doc_string', - 'resolve': 10 - } - } ], - 'completion_start_column': 3, - 'errors': [] - } - - def ResolveResponse( *args ): - return { - 'completion': { - 'insertion_text': 'insertion_text', - 'menu_text': 'menu_text', - 'extra_menu_info': 'extra_menu_info', - 'detailed_info': 'detailed_info', - 'kind': 'kind', - 'extra_data': { - 'doc_string': 'doc_string with more info' - } - }, - 'errors': [] - } - - current_buffer = VimBuffer( 'buffer' ) - with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): - with MockCompletionRequest( CompletionResponse ): - ycm.SendCompletionRequest() - assert_that( ycm.CompletionRequestReady() ) - response = ycm.GetCompletionResponse() - - post_vim_message.assert_not_called() - assert_that( - response, - has_entries( { - 'completions': contains_exactly( has_entries( { - 'word': 'insertion_text', - 'abbr': 'menu_text', - 'menu': 'extra_menu_info', - 'info': 'detailed_info\ndoc_string', - 'kind': 'k', - 'dup': 1, - 'empty': 1 - } ) ), - 'completion_start_column': 3 - } ) - ) - - item = response[ 'completions' ][ 0 ] - assert_that( json.loads( item[ 'user_data' ] ), - has_entries( { 'resolve': 10 } ) ) - - with MockResolveRequest( ResolveResponse ): - assert_that( ycm.ResolveCompletionItem( item ), equal_to( True ) ) - assert_that( ycm.CompletionRequestReady() ) - response = ycm.GetCompletionResponse() - post_vim_message.assert_not_called() - - assert_that( - response, - has_entries( { - 'completion': has_entries( { - 'word': 'insertion_text', - 'abbr': 'menu_text', - 'menu': 'extra_menu_info', - 'info': 'detailed_info\ndoc_string with more info', - 'kind': 'k', - 'dup': 1, - 'empty': 1 + 'errors': [] + } + + current_buffer = VimBuffer( 'buffer' ) + with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): + with MockCompletionRequest( CompletionResponse ): + ycm.SendCompletionRequest() + assert_that( ycm.CompletionRequestReady() ) + response = ycm.GetCompletionResponse() + + post_vim_message.assert_not_called() + assert_that( + response, + has_entries( { + 'completions': contains_exactly( has_entries( { + 'word': 'insertion_text', + 'abbr': 'menu_text', + 'menu': 'extra_menu_info', + 'info': 'detailed_info\ndoc_string', + 'kind': 'k', + 'dup': 1, + 'empty': 1 + } ) ), + 'completion_start_column': 3 + } ) + ) + + item = response[ 'completions' ][ 0 ] + assert_that( json.loads( item[ 'user_data' ] ), + has_entries( { 'resolve': 10 } ) ) + + with MockResolveRequest( ResolveResponse ): + assert_that( ycm.ResolveCompletionItem( item ), equal_to( True ) ) + assert_that( ycm.CompletionRequestReady() ) + response = ycm.GetCompletionResponse() + post_vim_message.assert_not_called() + + assert_that( + response, + has_entries( { + 'completion': has_entries( { + 'word': 'insertion_text', + 'abbr': 'menu_text', + 'menu': 'extra_menu_info', + 'info': 'detailed_info\ndoc_string with more info', + 'kind': 'k', + 'dup': 1, + 'empty': 1 + } ) } ) - } ) - ) - - item = response[ 'completion' ] - - with MockResolveRequest( ServerError( 'must not be called' ) ): - assert_that( ycm.ResolveCompletionItem( item ), equal_to( False ) ) - post_vim_message.assert_not_called() - - -@YouCompleteMeInstance() -@patch( 'ycm.client.base_request._logger', autospec = True ) -@patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) -def ResolveCompletionRequest_ResponseContainsErrors_test( post_vim_message, - logger, - ycm ): - - def CompletionResponse( *args ): - return { - 'completions': [ { - 'insertion_text': 'insertion_text', - 'menu_text': 'menu_text', - 'extra_menu_info': 'extra_menu_info', - 'detailed_info': 'detailed_info', - 'kind': 'kind', - 'extra_data': { - 'doc_string': 'doc_string', - 'resolve': 10 - } - } ], - 'completion_start_column': 3, - 'errors': [] - } - - def ResolveResponse( *args ): - return { - 'completion': { - 'insertion_text': 'insertion_text', - 'menu_text': 'menu_text', - 'extra_menu_info': 'extra_menu_info', - 'detailed_info': 'detailed_info', - 'kind': 'kind', - 'extra_data': { - 'doc_string': 'doc_string with more info' - } - }, - 'errors': [ { - 'exception': { - 'TYPE': 'Exception' + ) + + item = response[ 'completion' ] + + with MockResolveRequest( ServerError( 'must not be called' ) ): + assert_that( ycm.ResolveCompletionItem( item ), equal_to( False ) ) + post_vim_message.assert_not_called() + + + @YouCompleteMeInstance() + @patch( 'ycm.client.base_request._logger', autospec = True ) + @patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) + def test_ResolveCompletionRequest_ResponseContainsErrors( self, + ycm, + post_vim_message, + logger ): + + def CompletionResponse( *args ): + return { + 'completions': [ { + 'insertion_text': 'insertion_text', + 'menu_text': 'menu_text', + 'extra_menu_info': 'extra_menu_info', + 'detailed_info': 'detailed_info', + 'kind': 'kind', + 'extra_data': { + 'doc_string': 'doc_string', + 'resolve': 10 + } + } ], + 'completion_start_column': 3, + 'errors': [] + } + + def ResolveResponse( *args ): + return { + 'completion': { + 'insertion_text': 'insertion_text', + 'menu_text': 'menu_text', + 'extra_menu_info': 'extra_menu_info', + 'detailed_info': 'detailed_info', + 'kind': 'kind', + 'extra_data': { + 'doc_string': 'doc_string with more info' + } }, - 'message': 'message', - 'traceback': 'traceback' - } ] - } - - current_buffer = VimBuffer( 'buffer' ) - with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): - with MockCompletionRequest( CompletionResponse ): - ycm.SendCompletionRequest() - assert_that( ycm.CompletionRequestReady() ) - response = ycm.GetCompletionResponse() - - post_vim_message.assert_not_called() - assert_that( - response, - has_entries( { - 'completions': contains_exactly( has_entries( { - 'word': 'insertion_text', - 'abbr': 'menu_text', - 'menu': 'extra_menu_info', - 'info': 'detailed_info\ndoc_string', - 'kind': 'k', - 'dup': 1, - 'empty': 1 - } ) ), - 'completion_start_column': 3 - } ) - ) - - item = response[ 'completions' ][ 0 ] - assert_that( json.loads( item[ 'user_data' ] ), - has_entries( { 'resolve': 10 } ) ) - - with MockResolveRequest( ResolveResponse ): - assert_that( ycm.ResolveCompletionItem( item ), equal_to( True ) ) - assert_that( ycm.CompletionRequestReady() ) - response = ycm.GetCompletionResponse() - - post_vim_message.assert_has_exact_calls( [ - call( 'Exception: message', truncate = True ) - ] ) - assert_that( - response, - has_entries( { - 'completion': has_entries( { - 'word': 'insertion_text', - 'abbr': 'menu_text', - 'menu': 'extra_menu_info', - 'info': 'detailed_info\ndoc_string with more info', - 'kind': 'k', - 'dup': 1, - 'empty': 1 + 'errors': [ { + 'exception': { + 'TYPE': 'Exception' + }, + 'message': 'message', + 'traceback': 'traceback' + } ] + } + + current_buffer = VimBuffer( 'buffer' ) + with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): + with MockCompletionRequest( CompletionResponse ): + ycm.SendCompletionRequest() + assert_that( ycm.CompletionRequestReady() ) + response = ycm.GetCompletionResponse() + + post_vim_message.assert_not_called() + assert_that( + response, + has_entries( { + 'completions': contains_exactly( has_entries( { + 'word': 'insertion_text', + 'abbr': 'menu_text', + 'menu': 'extra_menu_info', + 'info': 'detailed_info\ndoc_string', + 'kind': 'k', + 'dup': 1, + 'empty': 1 + } ) ), + 'completion_start_column': 3 } ) - } ) - ) - - -@YouCompleteMeInstance() -@patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) -def ResolveCompletionItem_NoUserData_test( post_vim_message, ycm ): - def CompletionResponse( *args ): - return { - 'completions': [ { - 'insertion_text': 'insertion_text', - 'menu_text': 'menu_text', - 'extra_menu_info': 'extra_menu_info', - 'detailed_info': 'detailed_info', - 'kind': 'kind' - } ], - 'completion_start_column': 3, - 'errors': [] - } - - current_buffer = VimBuffer( 'buffer' ) - with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): - with MockCompletionRequest( CompletionResponse ): - ycm.SendCompletionRequest() - assert_that( ycm.CompletionRequestReady() ) - response = ycm.GetCompletionResponse() - - post_vim_message.assert_not_called() - assert_that( - response, - has_entries( { - 'completions': contains_exactly( has_entries( { - 'word': 'insertion_text', - 'abbr': 'menu_text', - 'menu': 'extra_menu_info', - 'info': 'detailed_info', - 'kind': 'k', - 'dup': 1, - 'empty': 1 - } ) ), - 'completion_start_column': 3 - } ) - ) - - item = response[ 'completions' ][ 0 ] - item.pop( 'user_data' ) - - with MockResolveRequest( ServerError( 'must not be called' ) ): - assert_that( ycm.ResolveCompletionItem( item ), equal_to( False ) ) - post_vim_message.assert_not_called() - - -@YouCompleteMeInstance() -def ResolveCompletionItem_NoRequest_test( ycm ): - assert_that( ycm.GetCurrentCompletionRequest(), equal_to( None ) ) - assert_that( ycm.ResolveCompletionItem( {} ), equal_to( False ) ) - - -@YouCompleteMeInstance() -@patch( 'ycm.client.base_request._logger', autospec = True ) -@patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) -def ResolveCompletionRequest_ServerError_test( post_vim_message, logger, ycm ): - - def ServerResponse( *args ): - return { - 'completions': [ { - 'insertion_text': 'insertion_text', - 'menu_text': 'menu_text', - 'extra_menu_info': 'extra_menu_info', - 'detailed_info': 'detailed_info', - 'kind': 'kind', - 'extra_data': { - 'doc_string': 'doc_string', - 'resolve': 10 - } - } ], - 'completion_start_column': 3, - 'errors': [] - } - - current_buffer = VimBuffer( 'buffer' ) - with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): - with MockCompletionRequest( ServerResponse ): - ycm.SendCompletionRequest() - assert_that( ycm.CompletionRequestReady() ) - response = ycm.GetCompletionResponse() - - post_vim_message.assert_not_called() - assert_that( - response, - has_entries( { - 'completions': contains_exactly( has_entries( { - 'word': 'insertion_text', - 'abbr': 'menu_text', - 'menu': 'extra_menu_info', - 'info': 'detailed_info\ndoc_string', - 'kind': 'k', - 'dup': 1, - 'empty': 1 - } ) ), - 'completion_start_column': 3 - } ) - ) - - item = response[ 'completions' ][ 0 ] - assert_that( json.loads( item[ 'user_data' ] ), - has_entries( { 'resolve': 10 } ) ) - - with MockResolveRequest( ServerError( 'Server error' ) ): - ycm.ResolveCompletionItem( item ) - assert_that( ycm.CompletionRequestReady() ) - response = ycm.GetCompletionResponse() - - logger.exception.assert_called_with( 'Error while handling server ' - 'response' ) - post_vim_message.assert_has_exact_calls( [ - call( 'Server error', truncate = True ) - ] ) + ) + + item = response[ 'completions' ][ 0 ] + assert_that( json.loads( item[ 'user_data' ] ), + has_entries( { 'resolve': 10 } ) ) + + with MockResolveRequest( ResolveResponse ): + assert_that( ycm.ResolveCompletionItem( item ), equal_to( True ) ) + assert_that( ycm.CompletionRequestReady() ) + response = ycm.GetCompletionResponse() + + post_vim_message.assert_has_exact_calls( [ + call( 'Exception: message', truncate = True ) + ] ) + assert_that( + response, + has_entries( { + 'completion': has_entries( { + 'word': 'insertion_text', + 'abbr': 'menu_text', + 'menu': 'extra_menu_info', + 'info': 'detailed_info\ndoc_string with more info', + 'kind': 'k', + 'dup': 1, + 'empty': 1 + } ) + } ) + ) + + + @YouCompleteMeInstance() + @patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) + def test_ResolveCompletionItem_NoUserData( self, ycm, post_vim_message ): + def CompletionResponse( *args ): + return { + 'completions': [ { + 'insertion_text': 'insertion_text', + 'menu_text': 'menu_text', + 'extra_menu_info': 'extra_menu_info', + 'detailed_info': 'detailed_info', + 'kind': 'kind' + } ], + 'completion_start_column': 3, + 'errors': [] + } + + current_buffer = VimBuffer( 'buffer' ) + with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): + with MockCompletionRequest( CompletionResponse ): + ycm.SendCompletionRequest() + assert_that( ycm.CompletionRequestReady() ) + response = ycm.GetCompletionResponse() + + post_vim_message.assert_not_called() + assert_that( + response, + has_entries( { + 'completions': contains_exactly( has_entries( { + 'word': 'insertion_text', + 'abbr': 'menu_text', + 'menu': 'extra_menu_info', + 'info': 'detailed_info', + 'kind': 'k', + 'dup': 1, + 'empty': 1 + } ) ), + 'completion_start_column': 3 + } ) + ) + + item = response[ 'completions' ][ 0 ] + item.pop( 'user_data' ) + + with MockResolveRequest( ServerError( 'must not be called' ) ): + assert_that( ycm.ResolveCompletionItem( item ), equal_to( False ) ) + post_vim_message.assert_not_called() + + + @YouCompleteMeInstance() + def test_ResolveCompletionItem_NoRequest( self, ycm ): + assert_that( ycm.GetCurrentCompletionRequest(), equal_to( None ) ) + assert_that( ycm.ResolveCompletionItem( {} ), equal_to( False ) ) + + + @YouCompleteMeInstance() + @patch( 'ycm.client.base_request._logger', autospec = True ) + @patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) + def test_ResolveCompletionRequest_ServerError( + self, ycm, post_vim_message, logger ): + + def ServerResponse( *args ): + return { + 'completions': [ { + 'insertion_text': 'insertion_text', + 'menu_text': 'menu_text', + 'extra_menu_info': 'extra_menu_info', + 'detailed_info': 'detailed_info', + 'kind': 'kind', + 'extra_data': { + 'doc_string': 'doc_string', + 'resolve': 10 + } + } ], + 'completion_start_column': 3, + 'errors': [] + } + + current_buffer = VimBuffer( 'buffer' ) + with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): + with MockCompletionRequest( ServerResponse ): + ycm.SendCompletionRequest() + assert_that( ycm.CompletionRequestReady() ) + response = ycm.GetCompletionResponse() + + post_vim_message.assert_not_called() + assert_that( + response, + has_entries( { + 'completions': contains_exactly( has_entries( { + 'word': 'insertion_text', + 'abbr': 'menu_text', + 'menu': 'extra_menu_info', + 'info': 'detailed_info\ndoc_string', + 'kind': 'k', + 'dup': 1, + 'empty': 1 + } ) ), + 'completion_start_column': 3 + } ) + ) + + item = response[ 'completions' ][ 0 ] + assert_that( json.loads( item[ 'user_data' ] ), + has_entries( { 'resolve': 10 } ) ) + + with MockResolveRequest( ServerError( 'Server error' ) ): + ycm.ResolveCompletionItem( item ) + assert_that( ycm.CompletionRequestReady() ) + response = ycm.GetCompletionResponse() + + logger.exception.assert_called_with( 'Error while handling server ' + 'response' ) + post_vim_message.assert_has_exact_calls( [ + call( 'Server error', truncate = True ) + ] ) diff --git a/python/ycm/tests/conftest.py b/python/ycm/tests/conftest.py deleted file mode 100644 index 71ca3a429a..0000000000 --- a/python/ycm/tests/conftest.py +++ /dev/null @@ -1,143 +0,0 @@ -# Copyright (C) 2016-2020 YouCompleteMe contributors -# -# 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 . - -from ycm.tests.test_utils import MockVimModule -MockVimModule() - -import contextlib -import pytest -import time -import warnings -from urllib.error import HTTPError, URLError - -from ycm.client.base_request import BaseRequest -from ycm.tests import test_utils -from ycm.youcompleteme import YouCompleteMe -from ycmd.utils import CloseStandardStreams, WaitUntilProcessIsTerminated - - -def setUpAndTearDown(): - # 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' ) - try: - yield - finally: - warnings.resetwarnings() - - -# The default options which are required for a working YouCompleteMe object. -DEFAULT_CLIENT_OPTIONS = { - # YCM options - 'g:ycm_log_level': 'info', - 'g:ycm_keep_logfiles': 0, - 'g:ycm_extra_conf_vim_data': [], - 'g:ycm_server_python_interpreter': '', - 'g:ycm_show_diagnostics_ui': 1, - 'g:ycm_enable_diagnostic_signs': 1, - 'g:ycm_enable_diagnostic_highlighting': 0, - 'g:ycm_echo_current_diagnostic': 1, - 'g:ycm_filter_diagnostics': {}, - 'g:ycm_always_populate_location_list': 0, - 'g:ycm_collect_identifiers_from_tags_files': 0, - 'g:ycm_seed_identifiers_with_syntax': 0, - 'g:ycm_goto_buffer_command': 'same-buffer', - 'g:ycm_update_diagnostics_in_insert_mode': 1, - # ycmd options - 'g:ycm_auto_trigger': 1, - 'g:ycm_min_num_of_chars_for_completion': 2, - 'g:ycm_semantic_triggers': {}, - 'g:ycm_filetype_specific_completion_to_disable': { 'gitcommit': 1 }, - 'g:ycm_max_num_candidates': 50, - 'g:ycm_max_diagnostics_to_display': 30, - 'g:ycm_disable_signature_help': 0, -} - - -@contextlib.contextmanager -def UserOptions( options ): - old_vim_options = test_utils.VIM_OPTIONS.copy() - test_utils.VIM_OPTIONS.update( DEFAULT_CLIENT_OPTIONS ) - test_utils.VIM_OPTIONS.update( options ) - try: - yield - finally: - test_utils.VIM_OPTIONS = old_vim_options - - -def _IsReady(): - return BaseRequest().GetDataFromHandler( 'ready' ) - - -def WaitUntilReady( timeout = 5 ): - expiration = time.time() + timeout - while True: - try: - if time.time() > expiration: - raise RuntimeError( 'Waited for the server to be ready ' - f'for { timeout } seconds, aborting.' ) - if _IsReady(): - return - except ( URLError, HTTPError ): - pass - finally: - time.sleep( 0.1 ) - - -def StopServer( ycm ): - try: - ycm.OnVimLeave() - WaitUntilProcessIsTerminated( ycm._server_popen ) - CloseStandardStreams( ycm._server_popen ) - except Exception: - pass - - -@pytest.fixture -def ycm( request ): - custom_options = request.param - with UserOptions( custom_options ): - ycm = YouCompleteMe() - WaitUntilReady() - ycm.CheckIfServerIsReady() - try: - test_utils.VIM_MATCHES_FOR_WINDOW.clear() - yield ycm - finally: - StopServer( ycm ) - - -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 - `DEFAULT_CLIENT_OPTIONS`. Use the optional parameter |custom_options| to give - additional options and/or override the already existing ones. - - Example usage: - - from ycm.tests import YouCompleteMeInstance - - @YouCompleteMeInstance( { 'log_level': 'debug', - 'keep_logfiles': 1 } ) - def Debug_test( ycm ): - ... - """ - return pytest.mark.parametrize( - 'ycm', - [ custom_options ], - ids = [ '' ], - indirect = True ) diff --git a/python/ycm/tests/diagnostic_filter_test.py b/python/ycm/tests/diagnostic_filter_test.py index 3591feb932..6aabec5362 100644 --- a/python/ycm/tests/diagnostic_filter_test.py +++ b/python/ycm/tests/diagnostic_filter_test.py @@ -19,6 +19,7 @@ MockVimModule() from hamcrest import assert_that, equal_to +from unittest import TestCase from ycm.diagnostic_filter import DiagnosticFilter @@ -45,91 +46,92 @@ def _CreateFilterForTypes( opts, types ): return DiagnosticFilter.CreateFromOptions( opts ).SubsetForTypes( types ) -def RegexFilter_test(): - opts = _JavaFilter( { 'regex' : 'taco' } ) - f = _CreateFilterForTypes( opts, [ 'java' ] ) +class DiagnosticFilterTest( TestCase ): + def test_RegexFilter( self ): + opts = _JavaFilter( { 'regex' : 'taco' } ) + f = _CreateFilterForTypes( opts, [ 'java' ] ) - _assert_rejects( f, 'This is a Taco' ) - _assert_accepts( f, 'This is a Burrito' ) + _assert_rejects( f, 'This is a Taco' ) + _assert_accepts( f, 'This is a Burrito' ) -def RegexSingleList_test(): - opts = _JavaFilter( { 'regex' : [ 'taco' ] } ) - f = _CreateFilterForTypes( opts, [ 'java' ] ) + def test_RegexSingleList( self ): + opts = _JavaFilter( { 'regex' : [ 'taco' ] } ) + f = _CreateFilterForTypes( opts, [ 'java' ] ) - _assert_rejects( f, 'This is a Taco' ) - _assert_accepts( f, 'This is a Burrito' ) + _assert_rejects( f, 'This is a Taco' ) + _assert_accepts( f, 'This is a Burrito' ) -def RegexMultiList_test(): - opts = _JavaFilter( { 'regex' : [ 'taco', 'burrito' ] } ) - f = _CreateFilterForTypes( opts, [ 'java' ] ) + def test_RegexMultiList( self ): + opts = _JavaFilter( { 'regex' : [ 'taco', 'burrito' ] } ) + f = _CreateFilterForTypes( opts, [ 'java' ] ) - _assert_rejects( f, 'This is a Taco' ) - _assert_rejects( f, 'This is a Burrito' ) + _assert_rejects( f, 'This is a Taco' ) + _assert_rejects( f, 'This is a Burrito' ) -def RegexNotFiltered_test(): - opts = _JavaFilter( { 'regex' : 'taco' } ) - f = _CreateFilterForTypes( opts, [ 'cs' ] ) + def test_RegexNotFiltered( self ): + opts = _JavaFilter( { 'regex' : 'taco' } ) + f = _CreateFilterForTypes( opts, [ 'cs' ] ) - _assert_accepts( f, 'This is a Taco' ) - _assert_accepts( f, 'This is a Burrito' ) + _assert_accepts( f, 'This is a Taco' ) + _assert_accepts( f, 'This is a Burrito' ) -def LevelWarnings_test(): - opts = _JavaFilter( { 'level' : 'warning' } ) - f = _CreateFilterForTypes( opts, [ 'java' ] ) + def test_LevelWarnings( self ): + opts = _JavaFilter( { 'level' : 'warning' } ) + f = _CreateFilterForTypes( opts, [ 'java' ] ) - _assert_rejects( f, { 'text' : 'This is an unimportant taco', - 'kind' : 'WARNING' } ) - _assert_accepts( f, { 'text' : 'This taco will be shown', - 'kind' : 'ERROR' } ) + _assert_rejects( f, { 'text' : 'This is an unimportant taco', + 'kind' : 'WARNING' } ) + _assert_accepts( f, { 'text' : 'This taco will be shown', + 'kind' : 'ERROR' } ) -def LevelErrors_test(): - opts = _JavaFilter( { 'level' : 'error' } ) - f = _CreateFilterForTypes( opts, [ 'java' ] ) + def test_LevelErrors( self ): + opts = _JavaFilter( { 'level' : 'error' } ) + f = _CreateFilterForTypes( opts, [ 'java' ] ) - _assert_accepts( f, { 'text' : 'This is an IMPORTANT taco', - 'kind' : 'WARNING' } ) - _assert_rejects( f, { 'text' : 'This taco will NOT be shown', - 'kind' : 'ERROR' } ) + _assert_accepts( f, { 'text' : 'This is an IMPORTANT taco', + 'kind' : 'WARNING' } ) + _assert_rejects( f, { 'text' : 'This taco will NOT be shown', + 'kind' : 'ERROR' } ) -def MultipleFilterTypesTypeTest_test(): + def test_MultipleFilterTypesTypeTest( self ): - opts = _JavaFilter( { 'regex' : '.*taco.*', - 'level' : 'warning' } ) - f = _CreateFilterForTypes( opts, [ 'java' ] ) + opts = _JavaFilter( { 'regex' : '.*taco.*', + 'level' : 'warning' } ) + f = _CreateFilterForTypes( opts, [ 'java' ] ) - _assert_rejects( f, { 'text' : 'This is an unimportant taco', - 'kind' : 'WARNING' } ) - _assert_rejects( f, { 'text' : 'This taco will NOT be shown', - 'kind' : 'ERROR' } ) - _assert_accepts( f, { 'text' : 'This burrito WILL be shown', - 'kind' : 'ERROR' } ) + _assert_rejects( f, { 'text' : 'This is an unimportant taco', + 'kind' : 'WARNING' } ) + _assert_rejects( f, { 'text' : 'This taco will NOT be shown', + 'kind' : 'ERROR' } ) + _assert_accepts( f, { 'text' : 'This burrito WILL be shown', + 'kind' : 'ERROR' } ) -def MergeMultipleFiletypes_test(): + def test_MergeMultipleFiletypes( self ): - opts = { 'filter_diagnostics' : { - 'java' : { 'regex' : '.*taco.*' }, - 'xml' : { 'regex' : '.*burrito.*' } } } + opts = { 'filter_diagnostics' : { + 'java' : { 'regex' : '.*taco.*' }, + 'xml' : { 'regex' : '.*burrito.*' } } } - f = _CreateFilterForTypes( opts, [ 'java', 'xml' ] ) + f = _CreateFilterForTypes( opts, [ 'java', 'xml' ] ) - _assert_rejects( f, 'This is a Taco' ) - _assert_rejects( f, 'This is a Burrito' ) - _assert_accepts( f, 'This is some Nachos' ) + _assert_rejects( f, 'This is a Taco' ) + _assert_rejects( f, 'This is a Burrito' ) + _assert_accepts( f, 'This is some Nachos' ) -def CommaSeparatedFiletypes_test(): + def test_CommaSeparatedFiletypes( self ): - opts = { 'filter_diagnostics' : { - 'java,c,cs' : { 'regex' : '.*taco.*' } } } + opts = { 'filter_diagnostics' : { + 'java,c,cs' : { 'regex' : '.*taco.*' } } } - f = _CreateFilterForTypes( opts, [ 'cs' ] ) + f = _CreateFilterForTypes( opts, [ 'cs' ] ) - _assert_rejects( f, 'This is a Taco' ) - _assert_accepts( f, 'This is a Burrito' ) + _assert_rejects( f, 'This is a Taco' ) + _assert_accepts( f, 'This is a Burrito' ) diff --git a/python/ycm/tests/event_notification_test.py b/python/ycm/tests/event_notification_test.py index 5c057f5efa..013407c7c4 100644 --- a/python/ycm/tests/event_notification_test.py +++ b/python/ycm/tests/event_notification_test.py @@ -32,6 +32,7 @@ from hamcrest import ( assert_that, contains_exactly, empty, equal_to, has_entries, has_entry, has_item, has_items, has_key, is_not ) +from unittest import TestCase from unittest.mock import call, MagicMock, patch @@ -85,183 +86,6 @@ def MockEventNotification( response_method, native_filetype_completer = True ): yield -@patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) -@YouCompleteMeInstance() -def EventNotification_FileReadyToParse_NonDiagnostic_Error_test( - post_vim_message, ycm ): - - # This test validates the behaviour of YouCompleteMe.HandleFileParseRequest - # in combination with YouCompleteMe.OnFileReadyToParse when the completer - # raises an exception handling FileReadyToParse event notification - ERROR_TEXT = 'Some completer response text' - - def ErrorResponse( *args ): - raise ServerError( ERROR_TEXT ) - - with MockArbitraryBuffer( 'some_filetype' ): - with MockEventNotification( ErrorResponse ): - ycm.OnFileReadyToParse() - assert_that( ycm.FileParseRequestReady() ) - ycm.HandleFileParseRequest() - - # The first call raises a warning - post_vim_message.assert_has_exact_calls( [ - call( ERROR_TEXT, truncate = True ) - ] ) - - # Subsequent calls don't re-raise the warning - ycm.HandleFileParseRequest() - post_vim_message.assert_has_exact_calls( [ - call( ERROR_TEXT, truncate = True ) - ] ) - - assert_that( not ycm.ShouldResendFileParseRequest() ) - - # But it does if a subsequent event raises again - ycm.OnFileReadyToParse() - assert_that( ycm.FileParseRequestReady() ) - ycm.HandleFileParseRequest() - post_vim_message.assert_has_exact_calls( [ - call( ERROR_TEXT, truncate = True ), - call( ERROR_TEXT, truncate = True ) - ] ) - - assert_that( not ycm.ShouldResendFileParseRequest() ) - - -@YouCompleteMeInstance() -def EventNotification_FileReadyToParse_NonDiagnostic_Error_NonNative_test( - ycm ): - - test_utils.VIM_MATCHES = [] - test_utils.VIM_SIGNS = [] - - with MockArbitraryBuffer( 'some_filetype' ): - with MockEventNotification( None, False ): - ycm.OnFileReadyToParse() - ycm.HandleFileParseRequest() - assert_that( test_utils.VIM_MATCHES, empty() ) - assert_that( test_utils.VIM_SIGNS, empty() ) - assert_that( not ycm.ShouldResendFileParseRequest() ) - - -@YouCompleteMeInstance() -def EventNotification_FileReadyToParse_NonDiagnostic_ConfirmExtraConf_test( - ycm ): - - # This test validates the behaviour of YouCompleteMe.HandleFileParseRequest - # in combination with YouCompleteMe.OnFileReadyToParse when the completer - # raises the (special) UnknownExtraConf exception - FILE_NAME = 'a_file' - MESSAGE = ( 'Found ' + FILE_NAME + '. Load? \n\n(Question can be ' - 'turned off with options, see YCM docs)' ) - - def UnknownExtraConfResponse( *args ): - raise UnknownExtraConf( FILE_NAME ) - - with patch( 'ycm.client.base_request.BaseRequest.PostDataToHandler', - new_callable = ExtendedMock ) as post_data_to_handler: - with MockArbitraryBuffer( 'some_filetype' ): - with MockEventNotification( UnknownExtraConfResponse ): - - # When the user accepts the extra conf, we load it - with patch( 'ycm.vimsupport.PresentDialog', - return_value = 0, - new_callable = ExtendedMock ) as present_dialog: - ycm.OnFileReadyToParse() - assert_that( ycm.FileParseRequestReady() ) - ycm.HandleFileParseRequest() - - present_dialog.assert_has_exact_calls( [ - PresentDialog_Confirm_Call( MESSAGE ), - ] ) - post_data_to_handler.assert_has_exact_calls( [ - call( { 'filepath': FILE_NAME }, 'load_extra_conf_file' ) - ] ) - - # Subsequent calls don't re-raise the warning - ycm.HandleFileParseRequest() - - present_dialog.assert_has_exact_calls( [ - PresentDialog_Confirm_Call( MESSAGE ) - ] ) - post_data_to_handler.assert_has_exact_calls( [ - call( { 'filepath': FILE_NAME }, 'load_extra_conf_file' ) - ] ) - - assert_that( ycm.ShouldResendFileParseRequest() ) - - # But it does if a subsequent event raises again - ycm.OnFileReadyToParse() - assert_that( ycm.FileParseRequestReady() ) - ycm.HandleFileParseRequest() - - present_dialog.assert_has_exact_calls( [ - PresentDialog_Confirm_Call( MESSAGE ), - PresentDialog_Confirm_Call( MESSAGE ), - ] ) - post_data_to_handler.assert_has_exact_calls( [ - call( { 'filepath': FILE_NAME }, 'load_extra_conf_file' ), - call( { 'filepath': FILE_NAME }, 'load_extra_conf_file' ) - ] ) - - assert_that( ycm.ShouldResendFileParseRequest() ) - - post_data_to_handler.reset_mock() - - # When the user rejects the extra conf, we reject it - with patch( 'ycm.vimsupport.PresentDialog', - return_value = 1, - new_callable = ExtendedMock ) as present_dialog: - ycm.OnFileReadyToParse() - assert_that( ycm.FileParseRequestReady() ) - ycm.HandleFileParseRequest() - - present_dialog.assert_has_exact_calls( [ - PresentDialog_Confirm_Call( MESSAGE ), - ] ) - post_data_to_handler.assert_has_exact_calls( [ - call( { 'filepath': FILE_NAME }, 'ignore_extra_conf_file' ) - ] ) - - # Subsequent calls don't re-raise the warning - ycm.HandleFileParseRequest() - - present_dialog.assert_has_exact_calls( [ - PresentDialog_Confirm_Call( MESSAGE ) - ] ) - post_data_to_handler.assert_has_exact_calls( [ - call( { 'filepath': FILE_NAME }, 'ignore_extra_conf_file' ) - ] ) - - assert_that( ycm.ShouldResendFileParseRequest() ) - - # But it does if a subsequent event raises again - ycm.OnFileReadyToParse() - assert_that( ycm.FileParseRequestReady() ) - ycm.HandleFileParseRequest() - - present_dialog.assert_has_exact_calls( [ - PresentDialog_Confirm_Call( MESSAGE ), - PresentDialog_Confirm_Call( MESSAGE ), - ] ) - post_data_to_handler.assert_has_exact_calls( [ - call( { 'filepath': FILE_NAME }, 'ignore_extra_conf_file' ), - call( { 'filepath': FILE_NAME }, 'ignore_extra_conf_file' ) - ] ) - - assert_that( ycm.ShouldResendFileParseRequest() ) - - -@YouCompleteMeInstance() -def EventNotification_FileReadyToParse_Diagnostic_Error_Native_test( ycm ): - test_utils.VIM_SIGNS = [] - - _Check_FileReadyToParse_Diagnostic_Error( ycm ) - _Check_FileReadyToParse_Diagnostic_Warning( ycm ) - _Check_FileReadyToParse_Diagnostic_Clean( ycm ) - - def _Check_FileReadyToParse_Diagnostic_Error( ycm ): # Tests Vim sign placement and error/warning count python API # when one error is returned. @@ -373,216 +197,397 @@ def _Check_FileReadyToParse_Diagnostic_Clean( ycm ): assert_that( not ycm.ShouldResendFileParseRequest() ) -@patch( 'ycm.youcompleteme.YouCompleteMe._AddUltiSnipsDataIfNeeded' ) -@YouCompleteMeInstance( { 'g:ycm_collect_identifiers_from_tags_files': 1 } ) -def EventNotification_FileReadyToParse_TagFiles_UnicodeWorkingDirectory_test( - add_ultisnips_data_if_needed, ycm ): - unicode_dir = PathToTestFile( 'uni¢od€' ) - current_buffer_file = PathToTestFile( 'uni¢𐍈d€', 'current_buffer' ) - current_buffer = VimBuffer( name = current_buffer_file, - contents = [ 'current_buffer_contents' ], - filetype = 'some_filetype' ) +class EventNotificationTest( TestCase ): + @patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) + @YouCompleteMeInstance() + def test_EventNotification_FileReadyToParse_NonDiagnostic_Error( + self, ycm, post_vim_message ): - with patch( 'ycm.client.event_notification.EventNotification.' - 'PostDataToHandlerAsync' ) as post_data_to_handler_async: - with CurrentWorkingDirectory( unicode_dir ): - with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 5 ) ): + # This test validates the behaviour of YouCompleteMe.HandleFileParseRequest + # in combination with YouCompleteMe.OnFileReadyToParse when the completer + # raises an exception handling FileReadyToParse event notification + ERROR_TEXT = 'Some completer response text' + + def ErrorResponse( *args ): + raise ServerError( ERROR_TEXT ) + + with MockArbitraryBuffer( 'some_filetype' ): + with MockEventNotification( ErrorResponse ): ycm.OnFileReadyToParse() + assert_that( ycm.FileParseRequestReady() ) + ycm.HandleFileParseRequest() - assert_that( - # Positional arguments passed to PostDataToHandlerAsync. - post_data_to_handler_async.call_args[ 0 ], - contains_exactly( - has_entries( { - 'filepath': current_buffer_file, - 'line_num': 1, - 'column_num': 6, - 'file_data': has_entries( { - current_buffer_file: has_entries( { - 'contents': 'current_buffer_contents\n', - 'filetypes': [ 'some_filetype' ] - } ) + # The first call raises a warning + post_vim_message.assert_has_exact_calls( [ + call( ERROR_TEXT, truncate = True ) + ] ) + + # Subsequent calls don't re-raise the warning + ycm.HandleFileParseRequest() + post_vim_message.assert_has_exact_calls( [ + call( ERROR_TEXT, truncate = True ) + ] ) + + assert_that( not ycm.ShouldResendFileParseRequest() ) + + # But it does if a subsequent event raises again + ycm.OnFileReadyToParse() + assert_that( ycm.FileParseRequestReady() ) + ycm.HandleFileParseRequest() + post_vim_message.assert_has_exact_calls( [ + call( ERROR_TEXT, truncate = True ), + call( ERROR_TEXT, truncate = True ) + ] ) + + assert_that( not ycm.ShouldResendFileParseRequest() ) + + + @YouCompleteMeInstance() + def test_EventNotification_FileReadyToParse_NonDiagnostic_Error_NonNative( + self, ycm ): + + test_utils.VIM_MATCHES = [] + test_utils.VIM_SIGNS = [] + + with MockArbitraryBuffer( 'some_filetype' ): + with MockEventNotification( None, False ): + ycm.OnFileReadyToParse() + ycm.HandleFileParseRequest() + assert_that( test_utils.VIM_MATCHES, empty() ) + assert_that( test_utils.VIM_SIGNS, empty() ) + assert_that( not ycm.ShouldResendFileParseRequest() ) + + + @YouCompleteMeInstance() + def test_EventNotification_FileReadyToParse_NonDiagnostic_ConfirmExtraConf( + self, ycm ): + + # This test validates the behaviour of YouCompleteMe.HandleFileParseRequest + # in combination with YouCompleteMe.OnFileReadyToParse when the completer + # raises the (special) UnknownExtraConf exception + FILE_NAME = 'a_file' + MESSAGE = ( 'Found ' + FILE_NAME + '. Load? \n\n(Question can be ' + 'turned off with options, see YCM docs)' ) + + def UnknownExtraConfResponse( *args ): + raise UnknownExtraConf( FILE_NAME ) + + with patch( 'ycm.client.base_request.BaseRequest.PostDataToHandler', + new_callable = ExtendedMock ) as post_data_to_handler: + with MockArbitraryBuffer( 'some_filetype' ): + with MockEventNotification( UnknownExtraConfResponse ): + + # When the user accepts the extra conf, we load it + with patch( 'ycm.vimsupport.PresentDialog', + return_value = 0, + new_callable = ExtendedMock ) as present_dialog: + ycm.OnFileReadyToParse() + assert_that( ycm.FileParseRequestReady() ) + ycm.HandleFileParseRequest() + + present_dialog.assert_has_exact_calls( [ + PresentDialog_Confirm_Call( MESSAGE ), + ] ) + post_data_to_handler.assert_has_exact_calls( [ + call( { 'filepath': FILE_NAME }, 'load_extra_conf_file' ) + ] ) + + # Subsequent calls don't re-raise the warning + ycm.HandleFileParseRequest() + + present_dialog.assert_has_exact_calls( [ + PresentDialog_Confirm_Call( MESSAGE ) + ] ) + post_data_to_handler.assert_has_exact_calls( [ + call( { 'filepath': FILE_NAME }, 'load_extra_conf_file' ) + ] ) + + assert_that( ycm.ShouldResendFileParseRequest() ) + + # But it does if a subsequent event raises again + ycm.OnFileReadyToParse() + assert_that( ycm.FileParseRequestReady() ) + ycm.HandleFileParseRequest() + + present_dialog.assert_has_exact_calls( [ + PresentDialog_Confirm_Call( MESSAGE ), + PresentDialog_Confirm_Call( MESSAGE ), + ] ) + post_data_to_handler.assert_has_exact_calls( [ + call( { 'filepath': FILE_NAME }, 'load_extra_conf_file' ), + call( { 'filepath': FILE_NAME }, 'load_extra_conf_file' ) + ] ) + + assert_that( ycm.ShouldResendFileParseRequest() ) + + post_data_to_handler.reset_mock() + + # When the user rejects the extra conf, we reject it + with patch( 'ycm.vimsupport.PresentDialog', + return_value = 1, + new_callable = ExtendedMock ) as present_dialog: + ycm.OnFileReadyToParse() + assert_that( ycm.FileParseRequestReady() ) + ycm.HandleFileParseRequest() + + present_dialog.assert_has_exact_calls( [ + PresentDialog_Confirm_Call( MESSAGE ), + ] ) + post_data_to_handler.assert_has_exact_calls( [ + call( { 'filepath': FILE_NAME }, 'ignore_extra_conf_file' ) + ] ) + + # Subsequent calls don't re-raise the warning + ycm.HandleFileParseRequest() + + present_dialog.assert_has_exact_calls( [ + PresentDialog_Confirm_Call( MESSAGE ) + ] ) + post_data_to_handler.assert_has_exact_calls( [ + call( { 'filepath': FILE_NAME }, 'ignore_extra_conf_file' ) + ] ) + + assert_that( ycm.ShouldResendFileParseRequest() ) + + # But it does if a subsequent event raises again + ycm.OnFileReadyToParse() + assert_that( ycm.FileParseRequestReady() ) + ycm.HandleFileParseRequest() + + present_dialog.assert_has_exact_calls( [ + PresentDialog_Confirm_Call( MESSAGE ), + PresentDialog_Confirm_Call( MESSAGE ), + ] ) + post_data_to_handler.assert_has_exact_calls( [ + call( { 'filepath': FILE_NAME }, 'ignore_extra_conf_file' ), + call( { 'filepath': FILE_NAME }, 'ignore_extra_conf_file' ) + ] ) + + assert_that( ycm.ShouldResendFileParseRequest() ) + + + @YouCompleteMeInstance() + def test_EventNotification_FileReadyToParse_Diagnostic_Error_Native( + self, ycm ): + test_utils.VIM_SIGNS = [] + + _Check_FileReadyToParse_Diagnostic_Error( ycm ) + _Check_FileReadyToParse_Diagnostic_Warning( ycm ) + _Check_FileReadyToParse_Diagnostic_Clean( ycm ) + + + @patch( 'ycm.youcompleteme.YouCompleteMe._AddUltiSnipsDataIfNeeded' ) + @YouCompleteMeInstance( { 'g:ycm_collect_identifiers_from_tags_files': 1 } ) + def test_EventNotification_FileReadyToParse_TagFiles_UnicodeWorkingDirectory( + self, ycm, *args ): + unicode_dir = PathToTestFile( 'uni¢od€' ) + current_buffer_file = PathToTestFile( 'uni¢𐍈d€', 'current_buffer' ) + current_buffer = VimBuffer( name = current_buffer_file, + contents = [ 'current_buffer_contents' ], + filetype = 'some_filetype' ) + + with patch( 'ycm.client.event_notification.EventNotification.' + 'PostDataToHandlerAsync' ) as post_data_to_handler_async: + with CurrentWorkingDirectory( unicode_dir ): + with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 5 ) ): + ycm.OnFileReadyToParse() + + assert_that( + # Positional arguments passed to PostDataToHandlerAsync. + post_data_to_handler_async.call_args[ 0 ], + contains_exactly( + has_entries( { + 'filepath': current_buffer_file, + 'line_num': 1, + 'column_num': 6, + 'file_data': has_entries( { + current_buffer_file: has_entries( { + 'contents': 'current_buffer_contents\n', + 'filetypes': [ 'some_filetype' ] + } ) + } ), + 'event_name': 'FileReadyToParse', + 'tag_files': has_item( PathToTestFile( 'uni¢od€', 'tags' ) ) } ), - 'event_name': 'FileReadyToParse', - 'tag_files': has_item( PathToTestFile( 'uni¢od€', 'tags' ) ) - } ), - 'event_notification' + 'event_notification' + ) ) - ) -@patch( 'ycm.youcompleteme.YouCompleteMe._AddUltiSnipsDataIfNeeded' ) -@YouCompleteMeInstance() -def EventNotification_BufferVisit_BuildRequestForCurrentAndUnsavedBuffers_test( - add_ultisnips_data_if_needed, ycm ): - - current_buffer_file = os.path.realpath( 'current_buffer' ) - current_buffer = VimBuffer( name = current_buffer_file, - number = 1, - contents = [ 'current_buffer_contents' ], - filetype = 'some_filetype', - modified = False ) - modified_buffer_file = os.path.realpath( 'modified_buffer' ) - modified_buffer = VimBuffer( name = modified_buffer_file, - number = 2, - contents = [ 'modified_buffer_contents' ], - filetype = 'some_filetype', - modified = True ) - - unmodified_buffer_file = os.path.realpath( 'unmodified_buffer' ) - unmodified_buffer = VimBuffer( name = unmodified_buffer_file, - number = 3, - contents = [ 'unmodified_buffer_contents' ], + @patch( 'ycm.youcompleteme.YouCompleteMe._AddUltiSnipsDataIfNeeded' ) + @YouCompleteMeInstance() + def test_EventNotification_BufferVisit_BuildRequestForCurrentAndUnsavedBuffers( # noqa + self, ycm, *args ): + + current_buffer_file = os.path.realpath( 'current_buffer' ) + current_buffer = VimBuffer( name = current_buffer_file, + number = 1, + contents = [ 'current_buffer_contents' ], + filetype = 'some_filetype', + modified = False ) + modified_buffer_file = os.path.realpath( 'modified_buffer' ) + modified_buffer = VimBuffer( name = modified_buffer_file, + number = 2, + contents = [ 'modified_buffer_contents' ], filetype = 'some_filetype', - modified = False ) + modified = True ) + + unmodified_buffer_file = os.path.realpath( 'unmodified_buffer' ) + unmodified_buffer = VimBuffer( name = unmodified_buffer_file, + number = 3, + contents = [ 'unmodified_buffer_contents' ], + filetype = 'some_filetype', + modified = False ) + + with patch( 'ycm.client.event_notification.EventNotification.' + 'PostDataToHandlerAsync' ) as post_data_to_handler_async: + with MockVimBuffers( [ current_buffer, + modified_buffer, + unmodified_buffer ], + [ current_buffer ], + ( 1, 5 ) ): + ycm.OnBufferVisit() - with patch( 'ycm.client.event_notification.EventNotification.' - 'PostDataToHandlerAsync' ) as post_data_to_handler_async: - with MockVimBuffers( [ current_buffer, modified_buffer, unmodified_buffer ], - [ current_buffer ], - ( 1, 5 ) ): - ycm.OnBufferVisit() + assert_that( + # Positional arguments passed to PostDataToHandlerAsync. + post_data_to_handler_async.call_args[ 0 ], + contains_exactly( + has_entries( { + 'filepath': current_buffer_file, + 'line_num': 1, + 'column_num': 6, + 'file_data': has_entries( { + current_buffer_file: has_entries( { + 'contents': 'current_buffer_contents\n', + 'filetypes': [ 'some_filetype' ] + } ), + modified_buffer_file: has_entries( { + 'contents': 'modified_buffer_contents\n', + 'filetypes': [ 'some_filetype' ] + } ) + } ), + 'event_name': 'BufferVisit' + } ), + 'event_notification' + ) + ) + + + @YouCompleteMeInstance() + def test_EventNotification_BufferUnload_BuildRequestForDeletedAndUnsavedBuffers( # noqa + self, ycm ): + current_buffer_file = os.path.realpath( 'current_βuffer' ) + current_buffer = VimBuffer( name = current_buffer_file, + number = 1, + contents = [ 'current_buffer_contents' ], + filetype = 'some_filetype', + modified = True ) + + deleted_buffer_file = os.path.realpath( 'deleted_βuffer' ) + deleted_buffer = VimBuffer( name = deleted_buffer_file, + number = 2, + contents = [ 'deleted_buffer_contents' ], + filetype = 'some_filetype', + modified = False ) + + with patch( 'ycm.client.event_notification.EventNotification.' + 'PostDataToHandlerAsync' ) as post_data_to_handler_async: + with MockVimBuffers( [ current_buffer, deleted_buffer ], + [ current_buffer ] ): + ycm.OnBufferUnload( deleted_buffer.number ) assert_that( # Positional arguments passed to PostDataToHandlerAsync. post_data_to_handler_async.call_args[ 0 ], contains_exactly( has_entries( { - 'filepath': current_buffer_file, + 'filepath': deleted_buffer_file, 'line_num': 1, - 'column_num': 6, + 'column_num': 1, 'file_data': has_entries( { current_buffer_file: has_entries( { 'contents': 'current_buffer_contents\n', 'filetypes': [ 'some_filetype' ] } ), - modified_buffer_file: has_entries( { - 'contents': 'modified_buffer_contents\n', + deleted_buffer_file: has_entries( { + 'contents': 'deleted_buffer_contents\n', 'filetypes': [ 'some_filetype' ] } ) } ), - 'event_name': 'BufferVisit' + 'event_name': 'BufferUnload' } ), 'event_notification' ) ) -@YouCompleteMeInstance() -def EventNotification_BufferUnload_BuildRequestForDeletedAndUnsavedBuffers_test( - ycm ): - current_buffer_file = os.path.realpath( 'current_βuffer' ) - current_buffer = VimBuffer( name = current_buffer_file, - number = 1, - contents = [ 'current_buffer_contents' ], - filetype = 'some_filetype', - modified = True ) - - deleted_buffer_file = os.path.realpath( 'deleted_βuffer' ) - deleted_buffer = VimBuffer( name = deleted_buffer_file, - number = 2, - contents = [ 'deleted_buffer_contents' ], - filetype = 'some_filetype', - modified = False ) - - with patch( 'ycm.client.event_notification.EventNotification.' - 'PostDataToHandlerAsync' ) as post_data_to_handler_async: - with MockVimBuffers( [ current_buffer, deleted_buffer ], - [ current_buffer ] ): - ycm.OnBufferUnload( deleted_buffer.number ) - - assert_that( - # Positional arguments passed to PostDataToHandlerAsync. - post_data_to_handler_async.call_args[ 0 ], - contains_exactly( - has_entries( { - 'filepath': deleted_buffer_file, - 'line_num': 1, - 'column_num': 1, - 'file_data': has_entries( { - current_buffer_file: has_entries( { - 'contents': 'current_buffer_contents\n', - 'filetypes': [ 'some_filetype' ] - } ), - deleted_buffer_file: has_entries( { - 'contents': 'deleted_buffer_contents\n', - 'filetypes': [ 'some_filetype' ] - } ) - } ), - 'event_name': 'BufferUnload' - } ), - 'event_notification' - ) - ) - - -@patch( 'ycm.vimsupport.CaptureVimCommand', return_value = """ + @patch( 'ycm.vimsupport.CaptureVimCommand', return_value = """ fooGroup xxx foo bar links to Statement""" ) -@YouCompleteMeInstance( { 'g:ycm_seed_identifiers_with_syntax': 1 } ) -def EventNotification_FileReadyToParse_SyntaxKeywords_SeedWithCache_test( - capture_vim_command, ycm ): + @YouCompleteMeInstance( { 'g:ycm_seed_identifiers_with_syntax': 1 } ) + def test_EventNotification_FileReadyToParse_SyntaxKeywords_SeedWithCache( + self, ycm, *args ): - current_buffer = VimBuffer( name = 'current_buffer', - filetype = 'some_filetype' ) + current_buffer = VimBuffer( name = 'current_buffer', + filetype = 'some_filetype' ) - with patch( 'ycm.client.event_notification.EventNotification.' - 'PostDataToHandlerAsync' ) as post_data_to_handler_async: - with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): - ycm.OnFileReadyToParse() - assert_that( - # Positional arguments passed to PostDataToHandlerAsync. - post_data_to_handler_async.call_args[ 0 ], - contains_exactly( - has_entry( 'syntax_keywords', has_items( 'foo', 'bar' ) ), - 'event_notification' + with patch( 'ycm.client.event_notification.EventNotification.' + 'PostDataToHandlerAsync' ) as post_data_to_handler_async: + with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): + ycm.OnFileReadyToParse() + assert_that( + # Positional arguments passed to PostDataToHandlerAsync. + post_data_to_handler_async.call_args[ 0 ], + contains_exactly( + has_entry( 'syntax_keywords', has_items( 'foo', 'bar' ) ), + 'event_notification' + ) ) - ) - # Do not send again syntax keywords in subsequent requests. - ycm.OnFileReadyToParse() - assert_that( - # Positional arguments passed to PostDataToHandlerAsync. - post_data_to_handler_async.call_args[ 0 ], - contains_exactly( - is_not( has_key( 'syntax_keywords' ) ), - 'event_notification' + # Do not send again syntax keywords in subsequent requests. + ycm.OnFileReadyToParse() + assert_that( + # Positional arguments passed to PostDataToHandlerAsync. + post_data_to_handler_async.call_args[ 0 ], + contains_exactly( + is_not( has_key( 'syntax_keywords' ) ), + 'event_notification' + ) ) - ) -@patch( 'ycm.vimsupport.CaptureVimCommand', return_value = """ + @patch( 'ycm.vimsupport.CaptureVimCommand', return_value = """ fooGroup xxx foo bar links to Statement""" ) -@YouCompleteMeInstance( { 'g:ycm_seed_identifiers_with_syntax': 1 } ) -def EventNotification_FileReadyToParse_SyntaxKeywords_ClearCacheIfRestart_test( - capture_vim_command, ycm ): + @YouCompleteMeInstance( { 'g:ycm_seed_identifiers_with_syntax': 1 } ) + def test_EventNotification_FileReadyToParse_SyntaxKeywords_ClearCacheIfRestart( # noqa + self, ycm, *args ): - current_buffer = VimBuffer( name = 'current_buffer', - filetype = 'some_filetype' ) + current_buffer = VimBuffer( name = 'current_buffer', + filetype = 'some_filetype' ) - with patch( 'ycm.client.event_notification.EventNotification.' - 'PostDataToHandlerAsync' ) as post_data_to_handler_async: - with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): - ycm.OnFileReadyToParse() - assert_that( - # Positional arguments passed to PostDataToHandlerAsync. - post_data_to_handler_async.call_args[ 0 ], - contains_exactly( - has_entry( 'syntax_keywords', has_items( 'foo', 'bar' ) ), - 'event_notification' + with patch( 'ycm.client.event_notification.EventNotification.' + 'PostDataToHandlerAsync' ) as post_data_to_handler_async: + with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): + ycm.OnFileReadyToParse() + assert_that( + # Positional arguments passed to PostDataToHandlerAsync. + post_data_to_handler_async.call_args[ 0 ], + contains_exactly( + has_entry( 'syntax_keywords', has_items( 'foo', 'bar' ) ), + 'event_notification' + ) ) - ) - # Send again the syntax keywords after restarting the server. - ycm.RestartServer() - WaitUntilReady() - ycm.OnFileReadyToParse() - assert_that( - # Positional arguments passed to PostDataToHandlerAsync. - post_data_to_handler_async.call_args[ 0 ], - contains_exactly( - has_entry( 'syntax_keywords', has_items( 'foo', 'bar' ) ), - 'event_notification' + # Send again the syntax keywords after restarting the server. + ycm.RestartServer() + WaitUntilReady() + ycm.OnFileReadyToParse() + assert_that( + # Positional arguments passed to PostDataToHandlerAsync. + post_data_to_handler_async.call_args[ 0 ], + contains_exactly( + has_entry( 'syntax_keywords', has_items( 'foo', 'bar' ) ), + 'event_notification' + ) ) - ) diff --git a/python/ycm/tests/omni_completer_test.py b/python/ycm/tests/omni_completer_test.py index cebe395bc2..b2428e5704 100644 --- a/python/ycm/tests/omni_completer_test.py +++ b/python/ycm/tests/omni_completer_test.py @@ -18,7 +18,7 @@ # along with YouCompleteMe. If not, see . from hamcrest import assert_that, contains_exactly, empty, has_entries -import pytest +from unittest import TestCase from ycm.tests.test_utils import MockVimBuffers, MockVimModule, VimBuffer MockVimModule() @@ -32,556 +32,537 @@ } -@YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 1, - 'g:ycm_semantic_triggers': TRIGGERS } ) -def OmniCompleter_GetCompletions_Cache_List_test( ycm ): - def Omnifunc( findstart, base ): - if findstart: - return 5 - return [ 'a', 'b', 'cdef' ] - - current_buffer = VimBuffer( 'buffer', - contents = [ 'test.' ], - filetype = FILETYPE, - omnifunc = Omnifunc ) - - with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 5 ) ): - ycm.SendCompletionRequest() - assert_that( - ycm.GetCompletionResponse(), - has_entries( { - 'completions': [ - { 'word': 'a', 'equal': 1 }, - { 'word': 'b', 'equal': 1 }, - { 'word': 'cdef', 'equal': 1 } - ], - 'completion_start_column': 6 - } ) - ) - - -@YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 1, - 'g:ycm_semantic_triggers': TRIGGERS } ) -def OmniCompleter_GetCompletions_Cache_ListFilter_test( ycm ): - def Omnifunc( findstart, base ): - if findstart: - return 5 - return [ 'a', 'b', 'cdef' ] - - current_buffer = VimBuffer( 'buffer', - contents = [ 'test.t' ], - filetype = FILETYPE, - omnifunc = Omnifunc ) - - with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 6 ) ): - ycm.SendCompletionRequest() - assert_that( - ycm.GetCompletionResponse(), - has_entries( { - 'completions': empty(), - 'completion_start_column': 6 - } ) - ) - - -@YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 0, - 'g:ycm_semantic_triggers': TRIGGERS } ) -def OmniCompleter_GetCompletions_NoCache_List_test( ycm ): - def Omnifunc( findstart, base ): - if findstart: - return 5 - return [ 'a', 'b', 'cdef' ] - - current_buffer = VimBuffer( 'buffer', - contents = [ 'test.' ], - filetype = FILETYPE, - omnifunc = Omnifunc ) - - with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 5 ) ): - ycm.SendCompletionRequest() - assert_that( - ycm.GetCompletionResponse(), - has_entries( { - 'completions': [ - { 'word': 'a', 'equal': 1 }, - { 'word': 'b', 'equal': 1 }, - { 'word': 'cdef', 'equal': 1 } - ], - 'completion_start_column': 6 - } ) - ) - - -@YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 0, - 'g:ycm_semantic_triggers': TRIGGERS } ) -def OmniCompleter_GetCompletions_NoCache_ListFilter_test( ycm ): - def Omnifunc( findstart, base ): - if findstart: - return 5 - return [ 'a', 'b', 'cdef' ] - - current_buffer = VimBuffer( 'buffer', - contents = [ 'test.t' ], - filetype = FILETYPE, - omnifunc = Omnifunc ) - - with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 6 ) ): - 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': [ - { 'word': 'a', 'equal': 1 }, - { 'word': 'b', 'equal': 1 }, - { 'word': 'cdef', 'equal': 1 } - ], - 'completion_start_column': 6 - } ) - ) - - -@YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 0, - 'g:ycm_semantic_triggers': TRIGGERS } ) -def OmniCompleter_GetCompletions_NoCache_UseFindStart_test( ycm ): - def Omnifunc( findstart, base ): - if findstart: - return 0 - return [ 'a', 'b', 'cdef' ] - - current_buffer = VimBuffer( 'buffer', - contents = [ 'test.t' ], - filetype = FILETYPE, - omnifunc = Omnifunc ) - - with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 6 ) ): - 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': [ - { 'word': 'a', 'equal': 1 }, - { 'word': 'b', 'equal': 1 }, - { 'word': 'cdef', 'equal': 1 } - ], - 'completion_start_column': 1 - } ) - ) - - -@YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 1, - 'g:ycm_semantic_triggers': TRIGGERS } ) -def OmniCompleter_GetCompletions_Cache_UseFindStart_test( ycm ): - def Omnifunc( findstart, base ): - if findstart: - return 0 - return [ 'a', 'b', 'cdef' ] - - current_buffer = VimBuffer( 'buffer', - contents = [ 'test.t' ], - filetype = FILETYPE, - omnifunc = Omnifunc ) - - with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 6 ) ): - ycm.SendCompletionRequest() - # There are no results because the query 'test.t' doesn't match any - # candidate (and cache_omnifunc=1, so we FilterAndSortCandidates). - assert_that( - ycm.GetCompletionResponse(), - has_entries( { - 'completions': empty(), - 'completion_start_column': 1 - } ) - ) - - -@YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 1, - 'g:ycm_semantic_triggers': TRIGGERS } ) -def OmniCompleter_GetCompletions_Cache_Object_test( ycm ): - def Omnifunc( findstart, base ): - if findstart: - return 5 - return { 'words': [ 'a', 'b', 'CDtEF' ] } - - current_buffer = VimBuffer( 'buffer', - contents = [ 'test.t' ], - filetype = FILETYPE, - omnifunc = Omnifunc ) - - with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 6 ) ): - ycm.SendCompletionRequest() - assert_that( - ycm.GetCompletionResponse(), - has_entries( { - 'completions': [ { 'word': 'CDtEF', 'equal': 1 } ], - 'completion_start_column': 6 - } ) - ) - - -@YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 1, - 'g:ycm_semantic_triggers': TRIGGERS } ) -def OmniCompleter_GetCompletions_Cache_ObjectList_test( ycm ): - 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' - } - ] - - current_buffer = VimBuffer( 'buffer', - contents = [ 'test.tt' ], - filetype = FILETYPE, - omnifunc = Omnifunc ) - - with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 7 ) ): - ycm.SendCompletionRequest() - assert_that( - ycm.GetCompletionResponse(), - has_entries( { - 'completions': contains_exactly( { - 'word' : 'test', - 'abbr' : 'ABBRTEST', - 'menu' : 'MENUTEST', - 'info' : 'INFOTEST', - 'kind' : 'T', - 'equal': 1 - } ), - 'completion_start_column': 6 - } ) - ) - - -@YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 0, - 'g:ycm_semantic_triggers': TRIGGERS } ) -def OmniCompleter_GetCompletions_NoCache_ObjectList_test( ycm ): - 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' - } - ] - - current_buffer = VimBuffer( 'buffer', - contents = [ 'test.tt' ], - filetype = FILETYPE, - omnifunc = Omnifunc ) - - with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 7 ) ): - 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!). - assert_that( - ycm.GetCompletionResponse(), - has_entries( { - 'completions': [ { - 'word' : 'a', - 'abbr' : 'ABBR', - 'menu' : 'MENU', - 'info' : 'INFO', - 'kind' : 'K', - 'equal': 1 - }, { - 'word' : 'test', - 'abbr' : 'ABBRTEST', - 'menu' : 'MENUTEST', - 'info' : 'INFOTEST', - 'kind' : 'T', - 'equal': 1 - } ], - 'completion_start_column': 6 - } ) - ) - - -@YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 1, - 'g:ycm_semantic_triggers': TRIGGERS } ) -def OmniCompleter_GetCompletions_Cache_ObjectListObject_test( ycm ): - def Omnifunc( findstart, base ): - if findstart: - return 5 - return { 'words': [ - { - 'word': 'a', - 'abbr': 'ABBR', - 'menu': 'MENU', - 'info': 'INFO', - 'kind': 'K' - }, - { - 'word': 'test', - 'abbr': 'ABBRTEST', - 'menu': 'MENUTEST', - 'info': 'INFOTEST', - 'kind': 'T' - } - ] } - - current_buffer = VimBuffer( 'buffer', - contents = [ 'test.tt' ], - filetype = FILETYPE, - omnifunc = Omnifunc ) - - with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 7 ) ): - ycm.SendCompletionRequest() - assert_that( - ycm.GetCompletionResponse(), - has_entries( { - 'completions': [ { - 'word' : 'test', - 'abbr' : 'ABBRTEST', - 'menu' : 'MENUTEST', - 'info' : 'INFOTEST', - 'kind' : 'T', - 'equal': 1 - } ], - 'completion_start_column': 6 - } ) - ) - - -@YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 0, - 'g:ycm_semantic_triggers': TRIGGERS } ) -def OmniCompleter_GetCompletions_NoCache_ObjectListObject_test( ycm ): - def Omnifunc( findstart, base ): - if findstart: - return 5 - return { 'words': [ - { - 'word': 'a', - 'abbr': 'ABBR', - 'menu': 'MENU', - 'info': 'INFO', - 'kind': 'K' - }, - { - 'word': 'test', - 'abbr': 'ABBRTEST', - 'menu': 'MENUTEST', - 'info': 'INFOTEST', - 'kind': 'T' - } - ] } - - current_buffer = VimBuffer( 'buffer', - contents = [ 'test.tt' ], - filetype = FILETYPE, - omnifunc = Omnifunc ) - - with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 7 ) ): - ycm.SendCompletionRequest() - # No FilterAndSortCandidates for cache_omnifunc=0 (we expect the omnifunc - # to do the filtering?) - assert_that( - ycm.GetCompletionResponse(), - has_entries( { - 'completions': [ { - 'word' : 'a', - 'abbr' : 'ABBR', - 'menu' : 'MENU', - 'info' : 'INFO', - 'kind' : 'K', - 'equal': 1 - }, { - 'word' : 'test', - 'abbr' : 'ABBRTEST', - 'menu' : 'MENUTEST', - 'info' : 'INFOTEST', - 'kind' : 'T', - 'equal': 1 - } ], - 'completion_start_column': 6 - } ) - ) - - -@YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 1, - 'g:ycm_semantic_triggers': TRIGGERS } ) -def OmniCompleter_GetCompletions_Cache_List_Unicode_test( ycm ): - def Omnifunc( findstart, base ): - if findstart: - return 12 - return [ '†est', 'å_unicode_identifier', 'πππππππ yummy πie' ] - - current_buffer = VimBuffer( 'buffer', - contents = [ '†åsty_π.' ], - filetype = FILETYPE, - omnifunc = Omnifunc ) - - with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 12 ) ): - ycm.SendCompletionRequest() - assert_that( - ycm.GetCompletionResponse(), - has_entries( { - 'completions': [ - { 'word': 'å_unicode_identifier', 'equal': 1 }, - { 'word': 'πππππππ yummy πie', 'equal': 1 }, - { 'word': '†est', 'equal': 1 } - ], - 'completion_start_column': 13 - } ) - ) - - -@YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 0, - 'g:ycm_semantic_triggers': TRIGGERS } ) -def OmniCompleter_GetCompletions_NoCache_List_Unicode_test( ycm ): - def Omnifunc( findstart, base ): - if findstart: - return 12 - return [ '†est', 'å_unicode_identifier', 'πππππππ yummy πie' ] - - current_buffer = VimBuffer( 'buffer', - contents = [ '†åsty_π.' ], - filetype = FILETYPE, - omnifunc = Omnifunc ) - - with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 12 ) ): - ycm.SendCompletionRequest() - assert_that( - ycm.GetCompletionResponse(), - has_entries( { - 'completions': [ - { 'word': '†est', 'equal': 1 }, - { 'word': 'å_unicode_identifier', 'equal': 1 }, - { 'word': 'πππππππ yummy πie', 'equal': 1 } - ], - 'completion_start_column': 13 - } ) - ) - - -@YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 1, - 'g:ycm_semantic_triggers': TRIGGERS } ) -def OmniCompleter_GetCompletions_Cache_List_Filter_Unicode_test( ycm ): - def Omnifunc( findstart, base ): - if findstart: - return 12 - return [ '†est', 'å_unicode_identifier', 'πππππππ yummy πie' ] - - current_buffer = VimBuffer( 'buffer', - contents = [ '†åsty_π.ππ' ], - filetype = FILETYPE, - omnifunc = Omnifunc ) - - with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 17 ) ): - ycm.SendCompletionRequest() - assert_that( - ycm.GetCompletionResponse(), - has_entries( { - 'completions': [ { 'word': 'πππππππ yummy πie', 'equal': 1 } ], - 'completion_start_column': 13 - } ) - ) - - -@YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 0, - 'g:ycm_semantic_triggers': TRIGGERS } ) -def OmniCompleter_GetCompletions_NoCache_List_Filter_Unicode_test( ycm ): +def StartColumnCompliance( ycm, + omnifunc_start_column, + ycm_completions, + ycm_start_column ): def Omnifunc( findstart, base ): if findstart: - return 12 - return [ 'πππππππ yummy πie' ] + return omnifunc_start_column + return [ 'foo' ] current_buffer = VimBuffer( 'buffer', - contents = [ '†åsty_π.ππ' ], + contents = [ 'fo' ], filetype = FILETYPE, omnifunc = Omnifunc ) - with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 17 ) ): - ycm.SendCompletionRequest() + with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 2 ) ): + ycm.SendCompletionRequest( force_semantic = True ) + r = ycm.GetCompletionResponse() assert_that( - ycm.GetCompletionResponse(), + r, has_entries( { - 'completions': [ { 'word': 'πππππππ yummy πie', 'equal': 1 } ], - 'completion_start_column': 13 + 'completions': ycm_completions, + 'completion_start_column': ycm_start_column } ) ) -@YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 1, - 'g:ycm_semantic_triggers': TRIGGERS } ) -def OmniCompleter_GetCompletions_Cache_ObjectList_Unicode_test( ycm ): - 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 = FILETYPE, - omnifunc = Omnifunc ) - - with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 17 ) ): - ycm.SendCompletionRequest() - assert_that( - ycm.GetCompletionResponse(), - has_entries( { - 'completions': [ { - 'word' : 'π†´ß†π', - 'abbr' : 'ÅııÂʉÍÊ', - 'menu' : '˜‰ˆËʉÍÊ', - 'info' : 'ȈÏØʉÍÊ', - 'kind' : 'Ê', - 'equal': 1 - } ], - 'completion_start_column': 13 - } ) - ) +class OmniCompleterTest( TestCase ): + @YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 1, + 'g:ycm_semantic_triggers': TRIGGERS } ) + def test_OmniCompleter_GetCompletions_Cache_List( self, ycm ): + def Omnifunc( findstart, base ): + if findstart: + return 5 + return [ 'a', 'b', 'cdef' ] + + current_buffer = VimBuffer( 'buffer', + contents = [ 'test.' ], + filetype = FILETYPE, + omnifunc = Omnifunc ) + + with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 5 ) ): + ycm.SendCompletionRequest() + assert_that( + ycm.GetCompletionResponse(), + has_entries( { + 'completions': [ + { 'word': 'a', 'equal': 1 }, + { 'word': 'b', 'equal': 1 }, + { 'word': 'cdef', 'equal': 1 } + ], + 'completion_start_column': 6 + } ) + ) + + + @YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 1, + 'g:ycm_semantic_triggers': TRIGGERS } ) + def test_OmniCompleter_GetCompletions_Cache_ListFilter( self, ycm ): + def Omnifunc( findstart, base ): + if findstart: + return 5 + return [ 'a', 'b', 'cdef' ] + + current_buffer = VimBuffer( 'buffer', + contents = [ 'test.t' ], + filetype = FILETYPE, + omnifunc = Omnifunc ) + + with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 6 ) ): + ycm.SendCompletionRequest() + assert_that( + ycm.GetCompletionResponse(), + has_entries( { + 'completions': empty(), + 'completion_start_column': 6 + } ) + ) + + + @YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 0, + 'g:ycm_semantic_triggers': TRIGGERS } ) + def test_OmniCompleter_GetCompletions_NoCache_List( self, ycm ): + def Omnifunc( findstart, base ): + if findstart: + return 5 + return [ 'a', 'b', 'cdef' ] + + current_buffer = VimBuffer( 'buffer', + contents = [ 'test.' ], + filetype = FILETYPE, + omnifunc = Omnifunc ) + + with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 5 ) ): + ycm.SendCompletionRequest() + assert_that( + ycm.GetCompletionResponse(), + has_entries( { + 'completions': [ + { 'word': 'a', 'equal': 1 }, + { 'word': 'b', 'equal': 1 }, + { 'word': 'cdef', 'equal': 1 } + ], + 'completion_start_column': 6 + } ) + ) + + + @YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 0, + 'g:ycm_semantic_triggers': TRIGGERS } ) + def test_OmniCompleter_GetCompletions_NoCache_ListFilter( self, ycm ): + def Omnifunc( findstart, base ): + if findstart: + return 5 + return [ 'a', 'b', 'cdef' ] + + current_buffer = VimBuffer( 'buffer', + contents = [ 'test.t' ], + filetype = FILETYPE, + omnifunc = Omnifunc ) + + with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 6 ) ): + 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': [ + { 'word': 'a', 'equal': 1 }, + { 'word': 'b', 'equal': 1 }, + { 'word': 'cdef', 'equal': 1 } + ], + 'completion_start_column': 6 + } ) + ) + + + @YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 0, + 'g:ycm_semantic_triggers': TRIGGERS } ) + def test_OmniCompleter_GetCompletions_NoCache_UseFindStart( self, ycm ): + def Omnifunc( findstart, base ): + if findstart: + return 0 + return [ 'a', 'b', 'cdef' ] + + current_buffer = VimBuffer( 'buffer', + contents = [ 'test.t' ], + filetype = FILETYPE, + omnifunc = Omnifunc ) + + with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 6 ) ): + 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': [ + { 'word': 'a', 'equal': 1 }, + { 'word': 'b', 'equal': 1 }, + { 'word': 'cdef', 'equal': 1 } + ], + 'completion_start_column': 1 + } ) + ) + + + @YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 1, + 'g:ycm_semantic_triggers': TRIGGERS } ) + def test_OmniCompleter_GetCompletions_Cache_UseFindStart( self, ycm ): + def Omnifunc( findstart, base ): + if findstart: + return 0 + return [ 'a', 'b', 'cdef' ] + + current_buffer = VimBuffer( 'buffer', + contents = [ 'test.t' ], + filetype = FILETYPE, + omnifunc = Omnifunc ) + + with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 6 ) ): + ycm.SendCompletionRequest() + # There are no results because the query 'test.t' doesn't match any + # candidate (and cache_omnifunc=1, so we FilterAndSortCandidates). + assert_that( + ycm.GetCompletionResponse(), + has_entries( { + 'completions': empty(), + 'completion_start_column': 1 + } ) + ) + + + @YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 1, + 'g:ycm_semantic_triggers': TRIGGERS } ) + def test_OmniCompleter_GetCompletions_Cache_Object( self, ycm ): + def Omnifunc( findstart, base ): + if findstart: + return 5 + return { 'words': [ 'a', 'b', 'CDtEF' ] } + + current_buffer = VimBuffer( 'buffer', + contents = [ 'test.t' ], + filetype = FILETYPE, + omnifunc = Omnifunc ) + + with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 6 ) ): + ycm.SendCompletionRequest() + assert_that( + ycm.GetCompletionResponse(), + has_entries( { + 'completions': [ { 'word': 'CDtEF', 'equal': 1 } ], + 'completion_start_column': 6 + } ) + ) + + + @YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 1, + 'g:ycm_semantic_triggers': TRIGGERS } ) + def test_OmniCompleter_GetCompletions_Cache_ObjectList( self, ycm ): + 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' + } + ] + current_buffer = VimBuffer( 'buffer', + contents = [ 'test.tt' ], + filetype = FILETYPE, + omnifunc = Omnifunc ) + + with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 7 ) ): + ycm.SendCompletionRequest() + assert_that( + ycm.GetCompletionResponse(), + has_entries( { + 'completions': contains_exactly( { + 'word' : 'test', + 'abbr' : 'ABBRTEST', + 'menu' : 'MENUTEST', + 'info' : 'INFOTEST', + 'kind' : 'T', + 'equal': 1 + } ), + 'completion_start_column': 6 + } ) + ) + + + @YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 0, + 'g:ycm_semantic_triggers': TRIGGERS } ) + def test_OmniCompleter_GetCompletions_NoCache_ObjectList( self, ycm ): + 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' + } + ] -@YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 1, - 'g:ycm_semantic_triggers': TRIGGERS } ) -def OmniCompleter_GetCompletions_Cache_ObjectListObject_Unicode_test( ycm ): - def Omnifunc( findstart, base ): - if findstart: - return 12 - return { - 'words': [ + current_buffer = VimBuffer( 'buffer', + contents = [ 'test.tt' ], + filetype = FILETYPE, + omnifunc = Omnifunc ) + + with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 7 ) ): + 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!). + assert_that( + ycm.GetCompletionResponse(), + has_entries( { + 'completions': [ { + 'word' : 'a', + 'abbr' : 'ABBR', + 'menu' : 'MENU', + 'info' : 'INFO', + 'kind' : 'K', + 'equal': 1 + }, { + 'word' : 'test', + 'abbr' : 'ABBRTEST', + 'menu' : 'MENUTEST', + 'info' : 'INFOTEST', + 'kind' : 'T', + 'equal': 1 + } ], + 'completion_start_column': 6 + } ) + ) + + + @YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 1, + 'g:ycm_semantic_triggers': TRIGGERS } ) + def test_OmniCompleter_GetCompletions_Cache_ObjectListObject( self, ycm ): + def Omnifunc( findstart, base ): + if findstart: + return 5 + return { 'words': [ + { + 'word': 'a', + 'abbr': 'ABBR', + 'menu': 'MENU', + 'info': 'INFO', + 'kind': 'K' + }, + { + 'word': 'test', + 'abbr': 'ABBRTEST', + 'menu': 'MENUTEST', + 'info': 'INFOTEST', + 'kind': 'T' + } + ] } + + current_buffer = VimBuffer( 'buffer', + contents = [ 'test.tt' ], + filetype = FILETYPE, + omnifunc = Omnifunc ) + + with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 7 ) ): + ycm.SendCompletionRequest() + assert_that( + ycm.GetCompletionResponse(), + has_entries( { + 'completions': [ { + 'word' : 'test', + 'abbr' : 'ABBRTEST', + 'menu' : 'MENUTEST', + 'info' : 'INFOTEST', + 'kind' : 'T', + 'equal': 1 + } ], + 'completion_start_column': 6 + } ) + ) + + + @YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 0, + 'g:ycm_semantic_triggers': TRIGGERS } ) + def test_OmniCompleter_GetCompletions_NoCache_ObjectListObject( self, ycm ): + def Omnifunc( findstart, base ): + if findstart: + return 5 + return { 'words': [ + { + 'word': 'a', + 'abbr': 'ABBR', + 'menu': 'MENU', + 'info': 'INFO', + 'kind': 'K' + }, + { + 'word': 'test', + 'abbr': 'ABBRTEST', + 'menu': 'MENUTEST', + 'info': 'INFOTEST', + 'kind': 'T' + } + ] } + + current_buffer = VimBuffer( 'buffer', + contents = [ 'test.tt' ], + filetype = FILETYPE, + omnifunc = Omnifunc ) + + with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 7 ) ): + ycm.SendCompletionRequest() + # No FilterAndSortCandidates for cache_omnifunc=0 (we expect the omnifunc + # to do the filtering?) + assert_that( + ycm.GetCompletionResponse(), + has_entries( { + 'completions': [ { + 'word' : 'a', + 'abbr' : 'ABBR', + 'menu' : 'MENU', + 'info' : 'INFO', + 'kind' : 'K', + 'equal': 1 + }, { + 'word' : 'test', + 'abbr' : 'ABBRTEST', + 'menu' : 'MENUTEST', + 'info' : 'INFOTEST', + 'kind' : 'T', + 'equal': 1 + } ], + 'completion_start_column': 6 + } ) + ) + + + @YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 1, + 'g:ycm_semantic_triggers': TRIGGERS } ) + def test_OmniCompleter_GetCompletions_Cache_List_Unicode( self, ycm ): + def Omnifunc( findstart, base ): + if findstart: + return 12 + return [ '†est', 'å_unicode_identifier', 'πππππππ yummy πie' ] + + current_buffer = VimBuffer( 'buffer', + contents = [ '†åsty_π.' ], + filetype = FILETYPE, + omnifunc = Omnifunc ) + + with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 12 ) ): + ycm.SendCompletionRequest() + assert_that( + ycm.GetCompletionResponse(), + has_entries( { + 'completions': [ + { 'word': 'å_unicode_identifier', 'equal': 1 }, + { 'word': 'πππππππ yummy πie', 'equal': 1 }, + { 'word': '†est', 'equal': 1 } + ], + 'completion_start_column': 13 + } ) + ) + + + @YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 0, + 'g:ycm_semantic_triggers': TRIGGERS } ) + def test_OmniCompleter_GetCompletions_NoCache_List_Unicode( self, ycm ): + def Omnifunc( findstart, base ): + if findstart: + return 12 + return [ '†est', 'å_unicode_identifier', 'πππππππ yummy πie' ] + + current_buffer = VimBuffer( 'buffer', + contents = [ '†åsty_π.' ], + filetype = FILETYPE, + omnifunc = Omnifunc ) + + with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 12 ) ): + ycm.SendCompletionRequest() + assert_that( + ycm.GetCompletionResponse(), + has_entries( { + 'completions': [ + { 'word': '†est', 'equal': 1 }, + { 'word': 'å_unicode_identifier', 'equal': 1 }, + { 'word': 'πππππππ yummy πie', 'equal': 1 } + ], + 'completion_start_column': 13 + } ) + ) + + + @YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 1, + 'g:ycm_semantic_triggers': TRIGGERS } ) + def test_OmniCompleter_GetCompletions_Cache_List_Filter_Unicode( self, ycm ): + def Omnifunc( findstart, base ): + if findstart: + return 12 + return [ '†est', 'å_unicode_identifier', 'πππππππ yummy πie' ] + + current_buffer = VimBuffer( 'buffer', + contents = [ '†åsty_π.ππ' ], + filetype = FILETYPE, + omnifunc = Omnifunc ) + + with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 17 ) ): + ycm.SendCompletionRequest() + assert_that( + ycm.GetCompletionResponse(), + has_entries( { + 'completions': [ { 'word': 'πππππππ yummy πie', 'equal': 1 } ], + 'completion_start_column': 13 + } ) + ) + + + @YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 0, + 'g:ycm_semantic_triggers': TRIGGERS } ) + def test_OmniCompleter_GetCompletions_NoCache_List_Filter_Unicode( + self, ycm ): + def Omnifunc( findstart, base ): + if findstart: + return 12 + return [ 'πππππππ yummy πie' ] + + current_buffer = VimBuffer( 'buffer', + contents = [ '†åsty_π.ππ' ], + filetype = FILETYPE, + omnifunc = Omnifunc ) + + with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 17 ) ): + ycm.SendCompletionRequest() + assert_that( + ycm.GetCompletionResponse(), + has_entries( { + 'completions': [ { 'word': 'πππππππ yummy πie', 'equal': 1 } ], + 'completion_start_column': 13 + } ) + ) + + + @YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 1, + 'g:ycm_semantic_triggers': TRIGGERS } ) + def test_OmniCompleter_GetCompletions_Cache_ObjectList_Unicode( self, ycm ): + def Omnifunc( findstart, base ): + if findstart: + return 12 + return [ { 'word': 'ålpha∫et', 'abbr': 'å∫∫®', @@ -595,352 +576,378 @@ def Omnifunc( findstart, base ): 'menu': '˜‰ˆËʉÍÊ', 'info': 'ȈÏØʉÍÊ', 'kind': 'Ê' - }, - { - 'word': 'test', - 'abbr': 'ÅııÂʉÍÊ', - 'menu': '˜‰ˆËʉÍÊ', - 'info': 'ȈÏØʉÍÊ', - 'kind': 'Ê' } ] - } - - current_buffer = VimBuffer( 'buffer', - contents = [ '†åsty_π.t' ], - filetype = FILETYPE, - omnifunc = Omnifunc ) - - with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 13 ) ): - ycm.SendCompletionRequest() - assert_that( - ycm.GetCompletionResponse(), - has_entries( { - 'completions': contains_exactly( { - 'word' : 'test', - 'abbr' : 'ÅııÂʉÍÊ', - 'menu' : '˜‰ˆËʉÍÊ', - 'info' : 'ȈÏØʉÍÊ', - 'kind' : 'Ê', - 'equal': 1 - }, { - 'word' : 'ålpha∫et', - 'abbr' : 'å∫∫®', - 'menu' : 'µ´~¨á', - 'info' : '^~fo', - 'kind' : '˚', - 'equal': 1 - } ), - 'completion_start_column': 13 - } ) - ) - - -@YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 1, - 'g:ycm_semantic_triggers': TRIGGERS } ) -def OmniCompleter_GetCompletions_RestoreCursorPositionAfterOmnifuncCall_test( - ycm ): - - # This omnifunc moves the cursor to the test definition like - # ccomplete#Complete would. - def Omnifunc( findstart, base ): - vimsupport.SetCurrentLineAndColumn( 0, 0 ) - if findstart: - return 5 - return [ 'length' ] - - current_buffer = VimBuffer( 'buffer', - contents = [ 'String test', - '', - 'test.' ], - filetype = FILETYPE, - omnifunc = Omnifunc ) - - with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 3, 5 ) ): - ycm.SendCompletionRequest() - assert_that( - vimsupport.CurrentLineAndColumn(), - contains_exactly( 2, 5 ) - ) - assert_that( - ycm.GetCompletionResponse(), - has_entries( { - 'completions': [ { 'word': 'length', 'equal': 1 } ], - 'completion_start_column': 6 - } ) - ) + current_buffer = VimBuffer( 'buffer', + contents = [ '†åsty_π.ππ' ], + filetype = FILETYPE, + omnifunc = Omnifunc ) + + with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 17 ) ): + ycm.SendCompletionRequest() + assert_that( + ycm.GetCompletionResponse(), + has_entries( { + 'completions': [ { + 'word' : 'π†´ß†π', + 'abbr' : 'ÅııÂʉÍÊ', + 'menu' : '˜‰ˆËʉÍÊ', + 'info' : 'ȈÏØʉÍÊ', + 'kind' : 'Ê', + 'equal': 1 + } ], + 'completion_start_column': 13 + } ) + ) + + + @YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 1, + 'g:ycm_semantic_triggers': TRIGGERS } ) + def test_OmniCompleter_GetCompletions_Cache_ObjectListObject_Unicode( + self, ycm ): + 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': 'Ê' + } + ] + } -@YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 1, - 'g:ycm_semantic_triggers': TRIGGERS } ) -def OmniCompleter_GetCompletions_MoveCursorPositionAtStartColumn_test( ycm ): - # This omnifunc relies on the cursor being moved at the start column when - # called the second time like LanguageClient#complete from the - # LanguageClient-neovim plugin. - def Omnifunc( findstart, base ): - if findstart: - return 5 - if vimsupport.CurrentColumn() == 5: + current_buffer = VimBuffer( 'buffer', + contents = [ '†åsty_π.t' ], + filetype = FILETYPE, + omnifunc = Omnifunc ) + + with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 13 ) ): + ycm.SendCompletionRequest() + assert_that( + ycm.GetCompletionResponse(), + has_entries( { + 'completions': contains_exactly( { + 'word' : 'test', + 'abbr' : 'ÅııÂʉÍÊ', + 'menu' : '˜‰ˆËʉÍÊ', + 'info' : 'ȈÏØʉÍÊ', + 'kind' : 'Ê', + 'equal': 1 + }, { + 'word' : 'ålpha∫et', + 'abbr' : 'å∫∫®', + 'menu' : 'µ´~¨á', + 'info' : '^~fo', + 'kind' : '˚', + 'equal': 1 + } ), + 'completion_start_column': 13 + } ) + ) + + + @YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 1, + 'g:ycm_semantic_triggers': TRIGGERS } ) + def test_OmniCompleter_GetCompletions_RestoreCursorPositionAfterOmnifuncCall( + self, ycm ): + + # This omnifunc moves the cursor to the test definition like + # ccomplete#Complete would. + def Omnifunc( findstart, base ): + vimsupport.SetCurrentLineAndColumn( 0, 0 ) + if findstart: + return 5 return [ 'length' ] - return [] - - current_buffer = VimBuffer( 'buffer', - contents = [ 'String test', - '', - 'test.le' ], - filetype = FILETYPE, - omnifunc = Omnifunc ) - - with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 3, 7 ) ): - ycm.SendCompletionRequest() - assert_that( - vimsupport.CurrentLineAndColumn(), - contains_exactly( 2, 7 ) - ) - assert_that( - ycm.GetCompletionResponse(), - has_entries( { - 'completions': [ { 'word': 'length', 'equal': 1 } ], - 'completion_start_column': 6 - } ) - ) - - -def StartColumnCompliance( ycm, - omnifunc_start_column, - ycm_completions, - ycm_start_column ): - def Omnifunc( findstart, base ): - if findstart: - return omnifunc_start_column - return [ 'foo' ] - - current_buffer = VimBuffer( 'buffer', - contents = [ 'fo' ], - filetype = FILETYPE, - omnifunc = Omnifunc ) - - with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 2 ) ): - ycm.SendCompletionRequest( force_semantic = True ) - assert_that( - ycm.GetCompletionResponse(), - has_entries( { - 'completions': ycm_completions, - 'completion_start_column': ycm_start_column - } ) - ) - - -@YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 1 } ) -@pytest.mark.parametrize( - 'omnifunc_start_column,ycm_completions,ycm_start_column', [ - [ -4, [ { 'word': 'foo', 'equal': 1 } ], 3 ], - [ -3, [], 1 ], - [ -2, [], 1 ], - [ -1, [ { 'word': 'foo', 'equal': 1 } ], 3 ], - [ 0, [ { 'word': 'foo', 'equal': 1 } ], 1 ], - [ 1, [ { 'word': 'foo', 'equal': 1 } ], 2 ], - [ 2, [ { 'word': 'foo', 'equal': 1 } ], 3 ], - [ 3, [ { 'word': 'foo', 'equal': 1 } ], 3 ] - ] ) -def OmniCompleter_GetCompletions_StartColumnCompliance_test( - ycm, - omnifunc_start_column, - ycm_completions, - ycm_start_column ): - StartColumnCompliance( ycm, - omnifunc_start_column, - ycm_completions, - ycm_start_column ) - - -@YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 0, - 'g:ycm_semantic_triggers': TRIGGERS } ) -def OmniCompleter_GetCompletions_NoCache_NoSemanticTrigger_test( ycm ): - def Omnifunc( findstart, base ): - if findstart: - return 0 - return [ 'test' ] - - current_buffer = VimBuffer( 'buffer', - contents = [ 'te' ], - filetype = FILETYPE, - omnifunc = Omnifunc ) - - with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 3 ) ): - ycm.SendCompletionRequest() - assert_that( - ycm.GetCompletionResponse(), - has_entries( { - 'completions': empty(), - 'completion_start_column': 1 - } ) - ) - - -@YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 0, - 'g:ycm_semantic_triggers': TRIGGERS } ) -def OmniCompleter_GetCompletions_NoCache_ForceSemantic_test( ycm ): - def Omnifunc( findstart, base ): - if findstart: - return 0 - return [ 'test' ] - - current_buffer = VimBuffer( 'buffer', - contents = [ 'te' ], - filetype = FILETYPE, - omnifunc = Omnifunc ) - - with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 3 ) ): - ycm.SendCompletionRequest( force_semantic = True ) - assert_that( - ycm.GetCompletionResponse(), - has_entries( { - 'completions': [ { 'word': 'test', 'equal': 1 } ], - 'completion_start_column': 1 - } ) - ) + current_buffer = VimBuffer( 'buffer', + contents = [ 'String test', + '', + 'test.' ], + filetype = FILETYPE, + omnifunc = Omnifunc ) + + with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 3, 5 ) ): + ycm.SendCompletionRequest() + assert_that( + vimsupport.CurrentLineAndColumn(), + contains_exactly( 2, 5 ) + ) + assert_that( + ycm.GetCompletionResponse(), + has_entries( { + 'completions': [ { 'word': 'length', 'equal': 1 } ], + 'completion_start_column': 6 + } ) + ) + + + @YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 1, + 'g:ycm_semantic_triggers': TRIGGERS } ) + def test_OmniCompleter_GetCompletions_MoveCursorPositionAtStartColumn( + self, ycm ): + # This omnifunc relies on the cursor being moved at the start column when + # called the second time like LanguageClient#complete from the + # LanguageClient-neovim plugin. + def Omnifunc( findstart, base ): + if findstart: + return 5 + if vimsupport.CurrentColumn() == 5: + return [ 'length' ] + return [] + + current_buffer = VimBuffer( 'buffer', + contents = [ 'String test', + '', + 'test.le' ], + filetype = FILETYPE, + omnifunc = Omnifunc ) + + with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 3, 7 ) ): + ycm.SendCompletionRequest() + assert_that( + vimsupport.CurrentLineAndColumn(), + contains_exactly( 2, 7 ) + ) + assert_that( + ycm.GetCompletionResponse(), + has_entries( { + 'completions': [ { 'word': 'length', 'equal': 1 } ], + 'completion_start_column': 6 + } ) + ) + + + @YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 0, } ) + def test_OmniCompleter_GetCompletions_StartColumnCompliance( self, ycm ): + for omnifunc_start_column, ycm_completions, ycm_start_column in [ + [ -4, [ { 'word': 'foo', 'equal': 1 } ], 3 ], + [ -3, [], 1 ], + [ -2, [], 1 ], + [ -1, [ { 'word': 'foo', 'equal': 1 } ], 3 ], + [ 0, [ { 'word': 'foo', 'equal': 1 } ], 1 ], + [ 1, [ { 'word': 'foo', 'equal': 1 } ], 2 ], + [ 2, [ { 'word': 'foo', 'equal': 1 } ], 3 ], + [ 3, [ { 'word': 'foo', 'equal': 1 } ], 3 ] + ]: + with self.subTest( omnifunc_start_column = omnifunc_start_column, + ycm_completions = ycm_completions, + ycm_start_column = ycm_start_column ): + StartColumnCompliance( ycm, + omnifunc_start_column, + ycm_completions, + ycm_start_column ) + + + @YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 0, + 'g:ycm_semantic_triggers': TRIGGERS } ) + def test_OmniCompleter_GetCompletions_NoCache_NoSemanticTrigger( self, ycm ): + def Omnifunc( findstart, base ): + if findstart: + return 0 + return [ 'test' ] + + current_buffer = VimBuffer( 'buffer', + contents = [ 'te' ], + filetype = FILETYPE, + omnifunc = Omnifunc ) + + with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 3 ) ): + ycm.SendCompletionRequest() + assert_that( + ycm.GetCompletionResponse(), + has_entries( { + 'completions': empty(), + 'completion_start_column': 1 + } ) + ) + + + @YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 0, + 'g:ycm_semantic_triggers': TRIGGERS } ) + def test_OmniCompleter_GetCompletions_NoCache_ForceSemantic( self, ycm ): + def Omnifunc( findstart, base ): + if findstart: + return 0 + return [ 'test' ] + + current_buffer = VimBuffer( 'buffer', + contents = [ 'te' ], + filetype = FILETYPE, + omnifunc = Omnifunc ) + + with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 3 ) ): + ycm.SendCompletionRequest( force_semantic = True ) + assert_that( + ycm.GetCompletionResponse(), + has_entries( { + 'completions': [ { 'word': 'test', 'equal': 1 } ], + 'completion_start_column': 1 + } ) + ) + + + @YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 1, + 'g:ycm_semantic_triggers': TRIGGERS } ) + def test_OmniCompleter_GetCompletions_ConvertStringsToDictionaries( + self, ycm ): + def Omnifunc( findstart, base ): + if findstart: + return 5 + return [ + { 'word': 'a' }, + 'b' + ] -@YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 1, - 'g:ycm_semantic_triggers': TRIGGERS } ) -def OmniCompleter_GetCompletions_ConvertStringsToDictionaries_test( ycm ): - def Omnifunc( findstart, base ): - if findstart: - return 5 - return [ - { 'word': 'a' }, - 'b' - ] - - current_buffer = VimBuffer( 'buffer', - contents = [ 'test.' ], - filetype = FILETYPE, - omnifunc = Omnifunc ) - - with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 7 ) ): - ycm.SendCompletionRequest() - assert_that( - ycm.GetCompletionResponse(), - has_entries( { - 'completions': [ - { 'word': 'a', 'equal': 1 }, - { 'word': 'b', 'equal': 1 } - ], - 'completion_start_column': 6 - } ) - ) - - -@YouCompleteMeInstance( { - 'g:ycm_cache_omnifunc': 0, - 'g:ycm_filetype_specific_completion_to_disable': { FILETYPE: 1 }, - 'g:ycm_semantic_triggers': TRIGGERS } ) -def OmniCompleter_GetCompletions_FiletypeDisabled_SemanticTrigger_test( ycm ): - def Omnifunc( findstart, base ): - if findstart: - return 5 - return [ 'a', 'b', 'cdef' ] - - current_buffer = VimBuffer( 'buffer', - contents = [ 'test.' ], - filetype = FILETYPE, - omnifunc = Omnifunc ) - - with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 6 ) ): - ycm.SendCompletionRequest() - assert_that( - ycm.GetCompletionResponse(), - has_entries( { - 'completions': empty(), - 'completion_start_column': 6 - } ) - ) - - -@YouCompleteMeInstance( { - 'g:ycm_cache_omnifunc': 0, - 'g:ycm_filetype_specific_completion_to_disable': { '*': 1 }, - 'g:ycm_semantic_triggers': TRIGGERS } ) -def OmniCompleter_GetCompletions_AllFiletypesDisabled_SemanticTrigger_test( - ycm ): - - def Omnifunc( findstart, base ): - if findstart: - return 5 - return [ 'a', 'b', 'cdef' ] - - current_buffer = VimBuffer( 'buffer', - contents = [ 'test.' ], - filetype = FILETYPE, - omnifunc = Omnifunc ) - - with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 6 ) ): - ycm.SendCompletionRequest() - assert_that( - ycm.GetCompletionResponse(), - has_entries( { - 'completions': empty(), - 'completion_start_column': 6 - } ) - ) - - -@YouCompleteMeInstance( { - 'g:ycm_cache_omnifunc': 0, - 'g:ycm_filetype_specific_completion_to_disable': { FILETYPE: 1 }, - 'g:ycm_semantic_triggers': TRIGGERS } ) -def OmniCompleter_GetCompletions_FiletypeDisabled_ForceSemantic_test( ycm ): - def Omnifunc( findstart, base ): - if findstart: - return 5 - return [ 'a', 'b', 'cdef' ] - - current_buffer = VimBuffer( 'buffer', - contents = [ 'test.' ], - filetype = FILETYPE, - omnifunc = Omnifunc ) - - with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 6 ) ): - ycm.SendCompletionRequest( force_semantic = True ) - assert_that( - ycm.GetCompletionResponse(), - has_entries( { - 'completions': [ - { 'word': 'a', 'equal': 1 }, - { 'word': 'b', 'equal': 1 }, - { 'word': 'cdef', 'equal': 1 } - ], - 'completion_start_column': 6 - } ) - ) - - -@YouCompleteMeInstance( { - 'g:ycm_cache_omnifunc': 0, - 'g:ycm_filetype_specific_completion_to_disable': { '*': 1 }, - 'g:ycm_semantic_triggers': TRIGGERS } ) -def OmniCompleter_GetCompletions_AllFiletypesDisabled_ForceSemantic_test( ycm ): - def Omnifunc( findstart, base ): - if findstart: - return 5 - return [ 'a', 'b', 'cdef' ] - - current_buffer = VimBuffer( 'buffer', - contents = [ 'test.' ], - filetype = FILETYPE, - omnifunc = Omnifunc ) - - with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 6 ) ): - ycm.SendCompletionRequest( force_semantic = True ) - assert_that( - ycm.GetCompletionResponse(), - has_entries( { - 'completions': [ - { 'word': 'a', 'equal': 1 }, - { 'word': 'b', 'equal': 1 }, - { 'word': 'cdef', 'equal': 1 } - ], - 'completion_start_column': 6 - } ) - ) + current_buffer = VimBuffer( 'buffer', + contents = [ 'test.' ], + filetype = FILETYPE, + omnifunc = Omnifunc ) + + with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 7 ) ): + ycm.SendCompletionRequest() + assert_that( + ycm.GetCompletionResponse(), + has_entries( { + 'completions': [ + { 'word': 'a', 'equal': 1 }, + { 'word': 'b', 'equal': 1 } + ], + 'completion_start_column': 6 + } ) + ) + + + @YouCompleteMeInstance( { + 'g:ycm_cache_omnifunc': 0, + 'g:ycm_filetype_specific_completion_to_disable': { FILETYPE: 1 }, + 'g:ycm_semantic_triggers': TRIGGERS } ) + def test_OmniCompleter_GetCompletions_FiletypeDisabled_SemanticTrigger( + self, ycm ): + def Omnifunc( findstart, base ): + if findstart: + return 5 + return [ 'a', 'b', 'cdef' ] + + current_buffer = VimBuffer( 'buffer', + contents = [ 'test.' ], + filetype = FILETYPE, + omnifunc = Omnifunc ) + + with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 6 ) ): + ycm.SendCompletionRequest() + assert_that( + ycm.GetCompletionResponse(), + has_entries( { + 'completions': empty(), + 'completion_start_column': 6 + } ) + ) + + + @YouCompleteMeInstance( { + 'g:ycm_cache_omnifunc': 0, + 'g:ycm_filetype_specific_completion_to_disable': { '*': 1 }, + 'g:ycm_semantic_triggers': TRIGGERS } ) + def test_OmniCompleter_GetCompletions_AllFiletypesDisabled_SemanticTrigger( + self, ycm ): + + def Omnifunc( findstart, base ): + if findstart: + return 5 + return [ 'a', 'b', 'cdef' ] + + current_buffer = VimBuffer( 'buffer', + contents = [ 'test.' ], + filetype = FILETYPE, + omnifunc = Omnifunc ) + + with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 6 ) ): + ycm.SendCompletionRequest() + assert_that( + ycm.GetCompletionResponse(), + has_entries( { + 'completions': empty(), + 'completion_start_column': 6 + } ) + ) + + + @YouCompleteMeInstance( { + 'g:ycm_cache_omnifunc': 0, + 'g:ycm_filetype_specific_completion_to_disable': { FILETYPE: 1 }, + 'g:ycm_semantic_triggers': TRIGGERS } ) + def test_OmniCompleter_GetCompletions_FiletypeDisabled_ForceSemantic( + self, ycm ): + def Omnifunc( findstart, base ): + if findstart: + return 5 + return [ 'a', 'b', 'cdef' ] + + current_buffer = VimBuffer( 'buffer', + contents = [ 'test.' ], + filetype = FILETYPE, + omnifunc = Omnifunc ) + + with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 6 ) ): + ycm.SendCompletionRequest( force_semantic = True ) + assert_that( + ycm.GetCompletionResponse(), + has_entries( { + 'completions': [ + { 'word': 'a', 'equal': 1 }, + { 'word': 'b', 'equal': 1 }, + { 'word': 'cdef', 'equal': 1 } + ], + 'completion_start_column': 6 + } ) + ) + + + @YouCompleteMeInstance( { + 'g:ycm_cache_omnifunc': 0, + 'g:ycm_filetype_specific_completion_to_disable': { '*': 1 }, + 'g:ycm_semantic_triggers': TRIGGERS } ) + def test_OmniCompleter_GetCompletions_AllFiletypesDisabled_ForceSemantic( + self, ycm ): + def Omnifunc( findstart, base ): + if findstart: + return 5 + return [ 'a', 'b', 'cdef' ] + + current_buffer = VimBuffer( 'buffer', + contents = [ 'test.' ], + filetype = FILETYPE, + omnifunc = Omnifunc ) + + with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 6 ) ): + ycm.SendCompletionRequest( force_semantic = True ) + assert_that( + ycm.GetCompletionResponse(), + has_entries( { + 'completions': [ + { 'word': 'a', 'equal': 1 }, + { 'word': 'b', 'equal': 1 }, + { 'word': 'cdef', 'equal': 1 } + ], + 'completion_start_column': 6 + } ) + ) diff --git a/python/ycm/tests/paths_test.py b/python/ycm/tests/paths_test.py index 31fe67eb4c..9a76351e0e 100644 --- a/python/ycm/tests/paths_test.py +++ b/python/ycm/tests/paths_test.py @@ -18,8 +18,8 @@ from ycm.tests.test_utils import MockVimModule MockVimModule() -import pytest from hamcrest import assert_that +from unittest import TestCase from ycm.paths import _EndsWithPython @@ -33,23 +33,26 @@ def EndsWithPython_Bad( path ): f'Path { path } does end with a Python name.' ) -@pytest.mark.parametrize( 'path', [ - 'python3', - '/usr/bin/python3.6', - '/home/user/.pyenv/shims/python3.6', - r'C:\Python36\python.exe' - ] ) -def EndsWithPython_Python3Paths_test( path ): - EndsWithPython_Good( path ) - - -@pytest.mark.parametrize( 'path', [ - None, - '', - '/opt/local/bin/vim', - r'C:\Program Files\Vim\vim74\gvim.exe', - '/usr/bin/python2.7', - '/home/user/.pyenv/shims/python3.2', - ] ) -def EndsWithPython_BadPaths_test( path ): - EndsWithPython_Bad( path ) +class PathTest( TestCase ): + def test_EndsWithPython_Python3Paths( self ): + for path in [ + 'python3', + '/usr/bin/python3.6', + '/home/user/.pyenv/shims/python3.6', + r'C:\Python36\python.exe' + ]: + with self.subTest( path = path ): + EndsWithPython_Good( path ) + + + def test_EndsWithPython_BadPaths( self ): + for path in [ + None, + '', + '/opt/local/bin/vim', + r'C:\Program Files\Vim\vim74\gvim.exe', + '/usr/bin/python2.7', + '/home/user/.pyenv/shims/python3.2', + ]: + with self.subTest( path = path ): + EndsWithPython_Bad( path ) diff --git a/python/ycm/tests/postcomplete_test.py b/python/ycm/tests/postcomplete_test.py index bc4c64112b..39eb5e5c82 100644 --- a/python/ycm/tests/postcomplete_test.py +++ b/python/ycm/tests/postcomplete_test.py @@ -23,6 +23,7 @@ import contextlib import json from hamcrest import assert_that, contains_exactly, empty, equal_to, none +from unittest import TestCase from unittest.mock import MagicMock, DEFAULT, patch from ycm import vimsupport @@ -128,309 +129,319 @@ def _SetUpCompleteDone( completions ): yield request -@patch( 'ycm.vimsupport.CurrentFiletypes', return_value = [ 'ycmtest' ] ) -def OnCompleteDone_DefaultFixIt_test( *args ): - request = CompletionRequest( None ) - request.Done = MagicMock( return_value = True ) - request._OnCompleteDone_Csharp = MagicMock() - request._OnCompleteDone_FixIt = MagicMock() - request.OnCompleteDone() - request._OnCompleteDone_Csharp.assert_not_called() - request._OnCompleteDone_FixIt.assert_called_once_with() +class PostcompleteTest( TestCase ): + @patch( 'ycm.vimsupport.CurrentFiletypes', return_value = [ 'ycmtest' ] ) + def test_OnCompleteDone_DefaultFixIt( self, *args ): + request = CompletionRequest( None ) + request.Done = MagicMock( return_value = True ) + request._OnCompleteDone_Csharp = MagicMock() + request._OnCompleteDone_FixIt = MagicMock() + request.OnCompleteDone() + request._OnCompleteDone_Csharp.assert_not_called() + request._OnCompleteDone_FixIt.assert_called_once_with() -@patch( 'ycm.vimsupport.CurrentFiletypes', return_value = [ 'cs' ] ) -def OnCompleteDone_CsharpFixIt_test( *args ): - request = CompletionRequest( None ) - request.Done = MagicMock( return_value = True ) - request._OnCompleteDone_Csharp = MagicMock() - request._OnCompleteDone_FixIt = MagicMock() - request.OnCompleteDone() - request._OnCompleteDone_Csharp.assert_called_once_with() - request._OnCompleteDone_FixIt.assert_not_called() + @patch( 'ycm.vimsupport.CurrentFiletypes', return_value = [ 'cs' ] ) + def test_OnCompleteDone_CsharpFixIt( self, *args ): + request = CompletionRequest( None ) + request.Done = MagicMock( return_value = True ) + request._OnCompleteDone_Csharp = MagicMock() + request._OnCompleteDone_FixIt = MagicMock() + request.OnCompleteDone() + request._OnCompleteDone_Csharp.assert_called_once_with() + request._OnCompleteDone_FixIt.assert_not_called() -@patch( 'ycm.vimsupport.CurrentFiletypes', return_value = [ 'ycmtest' ] ) -def OnCompleteDone_NoFixItIfNotDone_test( *args ): - request = CompletionRequest( None ) - request.Done = MagicMock( return_value = False ) - request._OnCompleteDone_Csharp = MagicMock() - request._OnCompleteDone_FixIt = MagicMock() - request.OnCompleteDone() - request._OnCompleteDone_Csharp.assert_not_called() - request._OnCompleteDone_FixIt.assert_not_called() + @patch( 'ycm.vimsupport.CurrentFiletypes', return_value = [ 'ycmtest' ] ) + def test_OnCompleteDone_NoFixItIfNotDone( self, *args ): + request = CompletionRequest( None ) + request.Done = MagicMock( return_value = False ) + request._OnCompleteDone_Csharp = MagicMock() + request._OnCompleteDone_FixIt = MagicMock() + request.OnCompleteDone() + request._OnCompleteDone_Csharp.assert_not_called() + request._OnCompleteDone_FixIt.assert_not_called() -@patch( 'ycm.vimsupport.CurrentFiletypes', return_value = [ 'ycmtest' ] ) -def OnCompleteDone_NoFixItForOmnifunc_test( *args ): - request = OmniCompletionRequest( 'omnifunc', None ) - request.Done = MagicMock( return_value = True ) - request._OnCompleteDone_Csharp = MagicMock() - request._OnCompleteDone_FixIt = MagicMock() - request.OnCompleteDone() - request._OnCompleteDone_Csharp.assert_not_called() - request._OnCompleteDone_FixIt.assert_not_called() + @patch( 'ycm.vimsupport.CurrentFiletypes', return_value = [ 'ycmtest' ] ) + def test_OnCompleteDone_NoFixItForOmnifunc( self, *args ): + request = OmniCompletionRequest( 'omnifunc', None ) + request.Done = MagicMock( return_value = True ) + request._OnCompleteDone_Csharp = MagicMock() + request._OnCompleteDone_FixIt = MagicMock() + request.OnCompleteDone() + request._OnCompleteDone_Csharp.assert_not_called() + request._OnCompleteDone_FixIt.assert_not_called() -def FilterToCompletedCompletions_MatchIsReturned_test(): - completions = [ BuildCompletion( insertion_text = 'Test' ) ] - result = _FilterToMatchingCompletions( CompleteItemIs( 'Test' ), completions ) - assert_that( list( result ), contains_exactly( {} ) ) + def test_FilterToCompletedCompletions_MatchIsReturned( self ): + completions = [ BuildCompletion( insertion_text = 'Test' ) ] + result = _FilterToMatchingCompletions( CompleteItemIs( 'Test' ), + completions ) + assert_that( list( result ), contains_exactly( {} ) ) -def FilterToCompletedCompletions_ShortTextDoesntRaise_test(): - completions = [ BuildCompletion( insertion_text = 'AAA' ) ] - result = _FilterToMatchingCompletions( CompleteItemIs( 'A' ), completions ) - assert_that( list( result ), empty() ) + def test_FilterToCompletedCompletions_ShortTextDoesntRaise( self ): + completions = [ BuildCompletion( insertion_text = 'AAA' ) ] + result = _FilterToMatchingCompletions( CompleteItemIs( 'A' ), completions ) + assert_that( list( result ), empty() ) -def FilterToCompletedCompletions_ExactMatchIsReturned_test(): - completions = [ BuildCompletion( insertion_text = 'Test' ) ] - result = _FilterToMatchingCompletions( CompleteItemIs( 'Test' ), completions ) - assert_that( list( result ), contains_exactly( {} ) ) + def test_FilterToCompletedCompletions_ExactMatchIsReturned( self ): + completions = [ BuildCompletion( insertion_text = 'Test' ) ] + result = _FilterToMatchingCompletions( CompleteItemIs( 'Test' ), + completions ) + assert_that( list( result ), contains_exactly( {} ) ) -def FilterToCompletedCompletions_NonMatchIsntReturned_test(): - completions = [ BuildCompletion( insertion_text = 'A' ) ] - result = _FilterToMatchingCompletions( CompleteItemIs( ' Quote' ), - completions ) - assert_that( list( result ), empty() ) + def test_FilterToCompletedCompletions_NonMatchIsntReturned( self ): + completions = [ BuildCompletion( insertion_text = 'A' ) ] + result = _FilterToMatchingCompletions( CompleteItemIs( ' Quote' ), + completions ) + assert_that( list( result ), empty() ) -def FilterToCompletedCompletions_Unicode_test(): - completions = [ BuildCompletion( insertion_text = '†es†' ) ] - result = _FilterToMatchingCompletions( CompleteItemIs( '†es†' ), - completions ) - assert_that( list( result ), contains_exactly( {} ) ) + def test_FilterToCompletedCompletions_Unicode( self ): + completions = [ BuildCompletion( insertion_text = '†es†' ) ] + result = _FilterToMatchingCompletions( CompleteItemIs( '†es†' ), + completions ) + assert_that( list( result ), contains_exactly( {} ) ) -def GetRequiredNamespaceImport_ReturnNoneForNoExtraData_test(): - assert_that( _GetRequiredNamespaceImport( {} ), none() ) + def test_GetRequiredNamespaceImport_ReturnNoneForNoExtraData( self ): + assert_that( _GetRequiredNamespaceImport( {} ), none() ) -def GetRequiredNamespaceImport_ReturnNamespaceFromExtraData_test(): - namespace = 'A_NAMESPACE' - assert_that( _GetRequiredNamespaceImport( - BuildCompletionNamespace( namespace )[ 'extra_data' ] ), - equal_to( namespace ) ) + def test_GetRequiredNamespaceImport_ReturnNamespaceFromExtraData( self ): + namespace = 'A_NAMESPACE' + assert_that( _GetRequiredNamespaceImport( + BuildCompletionNamespace( namespace )[ 'extra_data' ] ), + equal_to( namespace ) ) -@patch( 'ycm.vimsupport.GetVariableValue', - GetVariableValue_CompleteItemIs( 'Te' ) ) -def GetExtraDataUserMayHaveCompleted_ReturnEmptyIfPendingMatches_test( - *args ): - completions = [ BuildCompletionNamespace( None ) ] - with _SetupForCsharpCompletionDone( completions ) as request: - assert_that( request._GetExtraDataUserMayHaveCompleted(), empty() ) + @patch( 'ycm.vimsupport.GetVariableValue', + GetVariableValue_CompleteItemIs( 'Te' ) ) + def test_GetExtraDataUserMayHaveCompleted_ReturnEmptyIfPendingMatches( + *args ): + completions = [ BuildCompletionNamespace( None ) ] + with _SetupForCsharpCompletionDone( completions ) as request: + assert_that( request._GetExtraDataUserMayHaveCompleted(), empty() ) -def GetExtraDataUserMayHaveCompleted_ReturnMatchIfExactMatches_test( *args ): - info = [ 'NS', 'Test', 'Abbr', 'Menu', 'Info', 'Kind' ] - completions = [ BuildCompletionNamespace( *info ) ] - with _SetupForCsharpCompletionDone( completions ) as request: - with patch( 'ycm.vimsupport.GetVariableValue', - GetVariableValue_CompleteItemIs( *info[ 1: ] ) ): + def test_GetExtraDataUserMayHaveCompleted_ReturnMatchIfExactMatches( + self, *args ): + info = [ 'NS', 'Test', 'Abbr', 'Menu', 'Info', 'Kind' ] + completions = [ BuildCompletionNamespace( *info ) ] + with _SetupForCsharpCompletionDone( completions ) as request: + with patch( 'ycm.vimsupport.GetVariableValue', + GetVariableValue_CompleteItemIs( *info[ 1: ] ) ): + assert_that( request._GetExtraDataUserMayHaveCompleted(), + contains_exactly( completions[ 0 ][ 'extra_data' ] ) ) + + + def test_GetExtraDataUserMayHaveCompleted_ReturnMatchIfExactMatchesEvenIfPartial( self ): # noqa + info = [ 'NS', 'Test', 'Abbr', 'Menu', 'Info', 'Kind' ] + completions = [ BuildCompletionNamespace( *info ), + BuildCompletion( insertion_text = 'TestTest' ) ] + with _SetupForCsharpCompletionDone( completions ) as request: + with patch( 'ycm.vimsupport.GetVariableValue', + GetVariableValue_CompleteItemIs( *info[ 1: ] ) ): + assert_that( request._GetExtraDataUserMayHaveCompleted(), + contains_exactly( completions[ 0 ][ 'extra_data' ] ) ) + + + def test_GetExtraDataUserMayHaveCompleted_DontReturnMatchIfNoExactMatchesAndPartial( self ): # noqa + info = [ 'NS', 'Test', 'Abbr', 'Menu', 'Info', 'Kind' ] + completions = [ BuildCompletion( insertion_text = info[ 0 ] ), + BuildCompletion( insertion_text = 'TestTest' ) ] + with _SetupForCsharpCompletionDone( completions ) as request: + with patch( 'ycm.vimsupport.GetVariableValue', + GetVariableValue_CompleteItemIs( *info[ 1: ] ) ): + assert_that( request._GetExtraDataUserMayHaveCompleted(), empty() ) + + + @patch( 'ycm.vimsupport.GetVariableValue', + GetVariableValue_CompleteItemIs( 'Test' ) ) + def test_GetExtraDataUserMayHaveCompleted_ReturnMatchIfMatches( self, *args ): + completions = [ BuildCompletionNamespace( None ) ] + with _SetupForCsharpCompletionDone( completions ) as request: assert_that( request._GetExtraDataUserMayHaveCompleted(), contains_exactly( completions[ 0 ][ 'extra_data' ] ) ) -def GetExtraDataUserMayHaveCompleted_ReturnMatchIfExactMatchesEvenIfPartial_test(): # noqa - info = [ 'NS', 'Test', 'Abbr', 'Menu', 'Info', 'Kind' ] - completions = [ BuildCompletionNamespace( *info ), - BuildCompletion( insertion_text = 'TestTest' ) ] - with _SetupForCsharpCompletionDone( completions ) as request: - with patch( 'ycm.vimsupport.GetVariableValue', - GetVariableValue_CompleteItemIs( *info[ 1: ] ) ): - assert_that( request._GetExtraDataUserMayHaveCompleted(), - contains_exactly( completions[ 0 ][ 'extra_data' ] ) ) + @patch( 'ycm.vimsupport.GetVariableValue', + GetVariableValue_CompleteItemIs( + 'Test', + user_data=json.dumps( { + 'required_namespace_import': 'namespace1' } ) ) ) + def test_GetExtraDataUserMayHaveCompleted_UseUserData0( self, *args ): + # Identical completions but we specify the first one via user_data. + completions = [ + BuildCompletionNamespace( 'namespace1' ), + BuildCompletionNamespace( 'namespace2' ) + ] + + with _SetupForCsharpCompletionDone( completions ) as request: + assert_that( + request._GetExtraDataUserMayHaveCompleted(), + contains_exactly( + BuildCompletionNamespace( 'namespace1' )[ 'extra_data' ] ) ) + + + @patch( 'ycm.vimsupport.GetVariableValue', + GetVariableValue_CompleteItemIs( + 'Test', + user_data=json.dumps( { + 'required_namespace_import': 'namespace2' } ) ) ) + def test_GetExtraDataUserMayHaveCompleted_UseUserData1( self, *args ): + # Identical completions but we specify the second one via user_data. + completions = [ + BuildCompletionNamespace( 'namespace1' ), + BuildCompletionNamespace( 'namespace2' ) + ] + + with _SetupForCsharpCompletionDone( completions ) as request: + assert_that( + request._GetExtraDataUserMayHaveCompleted(), + contains_exactly( + BuildCompletionNamespace( 'namespace2' )[ 'extra_data' ] ) ) + + + @patch( 'ycm.vimsupport.GetVariableValue', + GetVariableValue_CompleteItemIs( 'Test', user_data='' ) ) + def test_GetExtraDataUserMayHaveCompleted_EmptyUserData( self, *args ): + # Identical completions but none is selected. + completions = [ + BuildCompletionNamespace( 'namespace1' ), + BuildCompletionNamespace( 'namespace2' ) + ] + + with _SetupForCsharpCompletionDone( completions ) as request: + assert_that( request._GetExtraDataUserMayHaveCompleted(), empty() ) -def GetExtraDataUserMayHaveCompleted_DontReturnMatchIfNoExactMatchesAndPartial_test(): # noqa - info = [ 'NS', 'Test', 'Abbr', 'Menu', 'Info', 'Kind' ] - completions = [ BuildCompletion( insertion_text = info[ 0 ] ), - BuildCompletion( insertion_text = 'TestTest' ) ] - with _SetupForCsharpCompletionDone( completions ) as request: - with patch( 'ycm.vimsupport.GetVariableValue', - GetVariableValue_CompleteItemIs( *info[ 1: ] ) ): - assert_that( request._GetExtraDataUserMayHaveCompleted(), empty() ) + @patch( 'ycm.vimsupport.GetVariableValue', + GetVariableValue_CompleteItemIs( 'Test' ) ) + def test_PostCompleteCsharp_EmptyDoesntInsertNamespace( self, *args ): + with _SetupForCsharpCompletionDone( [] ) as request: + request._OnCompleteDone_Csharp() + assert_that( not vimsupport.InsertNamespace.called ) + + + @patch( 'ycm.vimsupport.GetVariableValue', + GetVariableValue_CompleteItemIs( 'Test' ) ) + def test_PostCompleteCsharp_ExistingWithoutNamespaceDoesntInsertNamespace( + self, *args ): + completions = [ BuildCompletionNamespace( None ) ] + with _SetupForCsharpCompletionDone( completions ) as request: + request._OnCompleteDone_Csharp() + assert_that( not vimsupport.InsertNamespace.called ) + + + @patch( 'ycm.vimsupport.GetVariableValue', + GetVariableValue_CompleteItemIs( 'Test' ) ) + def test_PostCompleteCsharp_ValueDoesInsertNamespace( self, *args ): + namespace = 'A_NAMESPACE' + completions = [ BuildCompletionNamespace( namespace ) ] + with _SetupForCsharpCompletionDone( completions ) as request: + request._OnCompleteDone_Csharp() + vimsupport.InsertNamespace.assert_called_once_with( namespace ) + + + @patch( 'ycm.vimsupport.GetVariableValue', + GetVariableValue_CompleteItemIs( 'Test' ) ) + @patch( 'ycm.vimsupport.PresentDialog', return_value = 1 ) + def test_PostCompleteCsharp_InsertSecondNamespaceIfSelected( self, *args ): + namespace = 'A_NAMESPACE' + namespace2 = 'ANOTHER_NAMESPACE' + completions = [ + BuildCompletionNamespace( namespace ), + BuildCompletionNamespace( namespace2 ), + ] + with _SetupForCsharpCompletionDone( completions ) as request: + request._OnCompleteDone_Csharp() + vimsupport.InsertNamespace.assert_called_once_with( namespace2 ) + + + @patch( 'ycm.vimsupport.GetVariableValue', + GetVariableValue_CompleteItemIs( 'Test' ) ) + @patch( 'ycm.vimsupport.ReplaceChunks' ) + def test_PostCompleteFixIt_ApplyFixIt_NoFixIts( self, replace_chunks, *args ): + completions = [ + BuildCompletionFixIt( [] ) + ] + with _SetUpCompleteDone( completions ) as request: + request._OnCompleteDone_FixIt() + replace_chunks.assert_not_called() + + + @patch( 'ycm.vimsupport.GetVariableValue', + GetVariableValue_CompleteItemIs( 'Test' ) ) + @patch( 'ycm.vimsupport.ReplaceChunks' ) + def test_PostCompleteFixIt_ApplyFixIt_EmptyFixIt( + self, replace_chunks, *args ): + completions = [ + BuildCompletionFixIt( [ { 'chunks': [] } ] ) + ] + with _SetUpCompleteDone( completions ) as request: + request._OnCompleteDone_FixIt() + replace_chunks.assert_called_once_with( [], silent = True ) -@patch( 'ycm.vimsupport.GetVariableValue', - GetVariableValue_CompleteItemIs( 'Test' ) ) -def GetExtraDataUserMayHaveCompleted_ReturnMatchIfMatches_test( *args ): - completions = [ BuildCompletionNamespace( None ) ] - with _SetupForCsharpCompletionDone( completions ) as request: - assert_that( request._GetExtraDataUserMayHaveCompleted(), - contains_exactly( completions[ 0 ][ 'extra_data' ] ) ) - - -@patch( 'ycm.vimsupport.GetVariableValue', - GetVariableValue_CompleteItemIs( - 'Test', - user_data=json.dumps( { - 'required_namespace_import': 'namespace1' } ) ) ) -def GetExtraDataUserMayHaveCompleted_UseUserData0_test( *args ): - # Identical completions but we specify the first one via user_data. - completions = [ - BuildCompletionNamespace( 'namespace1' ), - BuildCompletionNamespace( 'namespace2' ) - ] - - with _SetupForCsharpCompletionDone( completions ) as request: - assert_that( request._GetExtraDataUserMayHaveCompleted(), - contains_exactly( - BuildCompletionNamespace( 'namespace1' )[ 'extra_data' ] ) ) - - -@patch( 'ycm.vimsupport.GetVariableValue', - GetVariableValue_CompleteItemIs( - 'Test', - user_data=json.dumps( { - 'required_namespace_import': 'namespace2' } ) ) ) -def GetExtraDataUserMayHaveCompleted_UseUserData1_test( *args ): - # Identical completions but we specify the second one via user_data. - completions = [ - BuildCompletionNamespace( 'namespace1' ), - BuildCompletionNamespace( 'namespace2' ) - ] - - with _SetupForCsharpCompletionDone( completions ) as request: - assert_that( request._GetExtraDataUserMayHaveCompleted(), - contains_exactly( - BuildCompletionNamespace( 'namespace2' )[ 'extra_data' ] ) ) - - -@patch( 'ycm.vimsupport.GetVariableValue', - GetVariableValue_CompleteItemIs( 'Test', user_data='' ) ) -def GetExtraDataUserMayHaveCompleted_EmptyUserData_test( *args ): - # Identical completions but none is selected. - completions = [ - BuildCompletionNamespace( 'namespace1' ), - BuildCompletionNamespace( 'namespace2' ) - ] - - with _SetupForCsharpCompletionDone( completions ) as request: - assert_that( request._GetExtraDataUserMayHaveCompleted(), empty() ) - - -@patch( 'ycm.vimsupport.GetVariableValue', - GetVariableValue_CompleteItemIs( 'Test' ) ) -def PostCompleteCsharp_EmptyDoesntInsertNamespace_test( *args ): - with _SetupForCsharpCompletionDone( [] ) as request: - request._OnCompleteDone_Csharp() - assert_that( not vimsupport.InsertNamespace.called ) - - -@patch( 'ycm.vimsupport.GetVariableValue', - GetVariableValue_CompleteItemIs( 'Test' ) ) -def PostCompleteCsharp_ExistingWithoutNamespaceDoesntInsertNamespace_test( - *args ): - completions = [ BuildCompletionNamespace( None ) ] - with _SetupForCsharpCompletionDone( completions ) as request: - request._OnCompleteDone_Csharp() - assert_that( not vimsupport.InsertNamespace.called ) - - -@patch( 'ycm.vimsupport.GetVariableValue', - GetVariableValue_CompleteItemIs( 'Test' ) ) -def PostCompleteCsharp_ValueDoesInsertNamespace_test( *args ): - namespace = 'A_NAMESPACE' - completions = [ BuildCompletionNamespace( namespace ) ] - with _SetupForCsharpCompletionDone( completions ) as request: - request._OnCompleteDone_Csharp() - vimsupport.InsertNamespace.assert_called_once_with( namespace ) - - -@patch( 'ycm.vimsupport.GetVariableValue', - GetVariableValue_CompleteItemIs( 'Test' ) ) -@patch( 'ycm.vimsupport.PresentDialog', return_value = 1 ) -def PostCompleteCsharp_InsertSecondNamespaceIfSelected_test( *args ): - namespace = 'A_NAMESPACE' - namespace2 = 'ANOTHER_NAMESPACE' - completions = [ - BuildCompletionNamespace( namespace ), - BuildCompletionNamespace( namespace2 ), - ] - with _SetupForCsharpCompletionDone( completions ) as request: - request._OnCompleteDone_Csharp() - vimsupport.InsertNamespace.assert_called_once_with( namespace2 ) - - -@patch( 'ycm.vimsupport.GetVariableValue', - GetVariableValue_CompleteItemIs( 'Test' ) ) -@patch( 'ycm.vimsupport.ReplaceChunks' ) -def PostCompleteFixIt_ApplyFixIt_NoFixIts_test( replace_chunks, *args ): - completions = [ - BuildCompletionFixIt( [] ) - ] - with _SetUpCompleteDone( completions ) as request: - request._OnCompleteDone_FixIt() - replace_chunks.assert_not_called() - - -@patch( 'ycm.vimsupport.GetVariableValue', - GetVariableValue_CompleteItemIs( 'Test' ) ) -@patch( 'ycm.vimsupport.ReplaceChunks' ) -def PostCompleteFixIt_ApplyFixIt_EmptyFixIt_test( replace_chunks, *args ): - completions = [ - BuildCompletionFixIt( [ { 'chunks': [] } ] ) - ] - with _SetUpCompleteDone( completions ) as request: - request._OnCompleteDone_FixIt() - replace_chunks.assert_called_once_with( [], silent = True ) - - -@patch( 'ycm.vimsupport.GetVariableValue', - GetVariableValue_CompleteItemIs( 'Test' ) ) -@patch( 'ycm.vimsupport.ReplaceChunks' ) -def PostCompleteFixIt_ApplyFixIt_NoFixIt_test( replace_chunks, *args ): - completions = [ - BuildCompletion() - ] - with _SetUpCompleteDone( completions ) as request: - request._OnCompleteDone_FixIt() - replace_chunks.assert_not_called() - - -@patch( 'ycm.vimsupport.GetVariableValue', - GetVariableValue_CompleteItemIs( 'Test' ) ) -@patch( 'ycm.vimsupport.ReplaceChunks' ) -def PostCompleteFixIt_ApplyFixIt_PickFirst_test( replace_chunks, *args ): - completions = [ - BuildCompletionFixIt( [ { 'chunks': 'one' } ] ), - BuildCompletionFixIt( [ { 'chunks': 'two' } ] ), - ] - with _SetUpCompleteDone( completions ) as request: - request._OnCompleteDone_FixIt() - replace_chunks.assert_called_once_with( 'one', silent = True ) - - -@patch( 'ycm.vimsupport.GetVariableValue', - GetVariableValue_CompleteItemIs( - 'Test', - user_data=json.dumps( { 'fixits': [ { 'chunks': 'one' } ] } ) ) ) -@patch( 'ycm.vimsupport.ReplaceChunks' ) -def PostCompleteFixIt_ApplyFixIt_PickFirstUserData_test( replace_chunks, - *args ): - completions = [ - BuildCompletionFixIt( [ { 'chunks': 'one' } ] ), - BuildCompletionFixIt( [ { 'chunks': 'two' } ] ), - ] - with _SetUpCompleteDone( completions ) as request: - request._OnCompleteDone_FixIt() - replace_chunks.assert_called_once_with( 'one', silent = True ) - - -@patch( 'ycm.vimsupport.GetVariableValue', - GetVariableValue_CompleteItemIs( - 'Test', - user_data=json.dumps( { 'fixits': [ { 'chunks': 'two' } ] } ) ) ) -@patch( 'ycm.vimsupport.ReplaceChunks' ) -def PostCompleteFixIt_ApplyFixIt_PickSecond_test( replace_chunks, *args ): - completions = [ - BuildCompletionFixIt( [ { 'chunks': 'one' } ] ), - BuildCompletionFixIt( [ { 'chunks': 'two' } ] ), - ] - with _SetUpCompleteDone( completions ) as request: - request._OnCompleteDone_FixIt() - replace_chunks.assert_called_once_with( 'two', silent = True ) + @patch( 'ycm.vimsupport.GetVariableValue', + GetVariableValue_CompleteItemIs( 'Test' ) ) + @patch( 'ycm.vimsupport.ReplaceChunks' ) + def test_PostCompleteFixIt_ApplyFixIt_NoFixIt( self, replace_chunks, *args ): + completions = [ + BuildCompletion() + ] + with _SetUpCompleteDone( completions ) as request: + request._OnCompleteDone_FixIt() + replace_chunks.assert_not_called() + + + @patch( 'ycm.vimsupport.GetVariableValue', + GetVariableValue_CompleteItemIs( 'Test' ) ) + @patch( 'ycm.vimsupport.ReplaceChunks' ) + def test_PostCompleteFixIt_ApplyFixIt_PickFirst( + self, replace_chunks, *args ): + completions = [ + BuildCompletionFixIt( [ { 'chunks': 'one' } ] ), + BuildCompletionFixIt( [ { 'chunks': 'two' } ] ), + ] + with _SetUpCompleteDone( completions ) as request: + request._OnCompleteDone_FixIt() + replace_chunks.assert_called_once_with( 'one', silent = True ) + + + @patch( 'ycm.vimsupport.GetVariableValue', + GetVariableValue_CompleteItemIs( + 'Test', + user_data=json.dumps( { 'fixits': [ { 'chunks': 'one' } ] } ) ) ) + @patch( 'ycm.vimsupport.ReplaceChunks' ) + def test_PostCompleteFixIt_ApplyFixIt_PickFirstUserData( self, + replace_chunks, + *args ): + completions = [ + BuildCompletionFixIt( [ { 'chunks': 'one' } ] ), + BuildCompletionFixIt( [ { 'chunks': 'two' } ] ), + ] + with _SetUpCompleteDone( completions ) as request: + request._OnCompleteDone_FixIt() + replace_chunks.assert_called_once_with( 'one', silent = True ) + + + @patch( 'ycm.vimsupport.GetVariableValue', + GetVariableValue_CompleteItemIs( + 'Test', + user_data=json.dumps( { 'fixits': [ { 'chunks': 'two' } ] } ) ) ) + @patch( 'ycm.vimsupport.ReplaceChunks' ) + def test_PostCompleteFixIt_ApplyFixIt_PickSecond( + self, replace_chunks, *args ): + completions = [ + BuildCompletionFixIt( [ { 'chunks': 'one' } ] ), + BuildCompletionFixIt( [ { 'chunks': 'two' } ] ), + ] + with _SetUpCompleteDone( completions ) as request: + request._OnCompleteDone_FixIt() + replace_chunks.assert_called_once_with( 'two', silent = True ) diff --git a/python/ycm/tests/signature_help_test.py b/python/ycm/tests/signature_help_test.py index f993376abc..6ff1b4bdee 100644 --- a/python/ycm/tests/signature_help_test.py +++ b/python/ycm/tests/signature_help_test.py @@ -17,20 +17,22 @@ from hamcrest import ( assert_that, empty ) +from unittest import TestCase from ycm import signature_help as sh -def MakeSignatureHelpBuffer_Empty_test(): - assert_that( sh._MakeSignatureHelpBuffer( {} ), empty() ) - assert_that( sh._MakeSignatureHelpBuffer( { - 'activeSignature': 0, - 'activeParameter': 0, - 'signatures': [] - } ), empty() ) - assert_that( sh._MakeSignatureHelpBuffer( { - 'activeSignature': 0, - 'activeParameter': 0, - } ), empty() ) - assert_that( sh._MakeSignatureHelpBuffer( { - 'signatures': [] - } ), empty() ) +class SignatureHelpTest( TestCase ): + def test_MakeSignatureHelpBuffer_Empty( self ): + assert_that( sh._MakeSignatureHelpBuffer( {} ), empty() ) + assert_that( sh._MakeSignatureHelpBuffer( { + 'activeSignature': 0, + 'activeParameter': 0, + 'signatures': [] + } ), empty() ) + assert_that( sh._MakeSignatureHelpBuffer( { + 'activeSignature': 0, + 'activeParameter': 0, + } ), empty() ) + assert_that( sh._MakeSignatureHelpBuffer( { + 'signatures': [] + } ), empty() ) diff --git a/python/ycm/tests/syntax_parse_test.py b/python/ycm/tests/syntax_parse_test.py index a807cd7adc..7cd0e9ea91 100644 --- a/python/ycm/tests/syntax_parse_test.py +++ b/python/ycm/tests/syntax_parse_test.py @@ -21,6 +21,7 @@ import os from hamcrest import assert_that, contains_inanyorder, has_item, has_items +from unittest import TestCase from ycm import syntax_parse from ycmd.utils import ReadFile @@ -31,191 +32,196 @@ def ContentsOfTestFile( test_file ): return ReadFile( full_path_to_test_file ) -def KeywordsFromSyntaxListOutput_PythonSyntax_test(): - expected_keywords = ( - 'bytearray', 'IndexError', 'all', 'help', 'vars', 'SyntaxError', 'global', - 'elif', 'unicode', 'sorted', 'memoryview', 'isinstance', 'except', - 'nonlocal', 'NameError', 'finally', 'BytesWarning', 'dict', 'IOError', - 'pass', 'oct', 'bin', 'SystemExit', 'return', 'StandardError', 'format', - 'TabError', 'break', 'next', 'not', 'UnicodeDecodeError', 'False', - 'RuntimeWarning', 'list', 'iter', 'try', 'reload', 'Warning', 'round', - 'dir', 'cmp', 'set', 'bytes', 'UnicodeTranslateError', 'intern', - 'issubclass', 'yield', 'Ellipsis', 'hash', 'locals', 'BufferError', - 'slice', 'for', 'FloatingPointError', 'sum', 'VMSError', 'getattr', 'abs', - 'print', 'import', 'True', 'FutureWarning', 'ImportWarning', 'None', - 'EOFError', 'len', 'frozenset', 'ord', 'super', 'raise', 'TypeError', - 'KeyboardInterrupt', 'UserWarning', 'filter', 'range', 'staticmethod', - 'SystemError', 'or', 'BaseException', 'pow', 'RuntimeError', 'float', - 'MemoryError', 'StopIteration', 'globals', 'divmod', 'enumerate', 'apply', - 'LookupError', 'open', 'basestring', 'from', 'UnicodeError', 'zip', 'hex', - 'long', 'IndentationError', 'int', 'chr', '__import__', 'type', - 'Exception', 'continue', 'tuple', 'reduce', 'reversed', 'else', 'assert', - 'UnicodeEncodeError', 'input', 'with', 'hasattr', 'delattr', 'setattr', - 'raw_input', 'PendingDeprecationWarning', 'compile', 'ArithmeticError', - 'while', 'del', 'str', 'property', 'def', 'and', 'GeneratorExit', - 'ImportError', 'xrange', 'is', 'EnvironmentError', 'KeyError', 'coerce', - 'SyntaxWarning', 'file', 'in', 'unichr', 'ascii', 'any', 'as', 'if', - 'OSError', 'DeprecationWarning', 'min', 'UnicodeWarning', 'execfile', 'id', - 'complex', 'bool', 'ValueError', 'NotImplemented', 'map', 'exec', 'buffer', - 'max', 'class', 'object', 'repr', 'callable', 'ZeroDivisionError', 'eval', - '__debug__', 'ReferenceError', 'AssertionError', 'classmethod', - 'UnboundLocalError', 'NotImplementedError', 'lambda', 'AttributeError', - 'OverflowError', 'WindowsError' ) - - assert_that( syntax_parse._KeywordsFromSyntaxListOutput( - ContentsOfTestFile( 'python_syntax' ) ), - contains_inanyorder( *expected_keywords ) ) - - -def KeywordsFromSyntaxListOutput_CppSyntax_test(): - expected_keywords = ( - 'int_fast32_t', 'FILE', 'size_t', 'bitor', 'typedef', 'const', 'struct', - 'uint8_t', 'fpos_t', 'thread_local', 'unsigned', 'uint_least16_t', 'do', - 'intptr_t', 'uint_least64_t', 'return', 'auto', 'void', '_Complex', - 'break', '_Alignof', 'not', 'using', '_Static_assert', '_Thread_local', - 'public', 'uint_fast16_t', 'this', 'continue', 'char32_t', 'int16_t', - 'intmax_t', 'static', 'clock_t', 'sizeof', 'int_fast64_t', 'mbstate_t', - 'try', 'xor', 'uint_fast32_t', 'int_least8_t', 'div_t', 'volatile', - 'template', 'char16_t', 'new', 'ldiv_t', 'int_least16_t', 'va_list', - 'uint_least8_t', 'goto', 'noreturn', 'enum', 'static_assert', 'bitand', - 'compl', 'imaginary', 'jmp_buf', 'throw', 'asm', 'ptrdiff_t', 'uint16_t', - 'or', 'uint_fast8_t', '_Bool', 'int32_t', 'float', 'private', 'restrict', - 'wint_t', 'operator', 'not_eq', '_Imaginary', 'alignas', 'union', 'long', - 'uint_least32_t', 'int_least64_t', 'friend', 'uintptr_t', 'int8_t', 'else', - 'export', 'int_fast8_t', 'catch', 'true', 'case', 'default', 'double', - '_Noreturn', 'signed', 'typename', 'while', 'protected', 'wchar_t', - 'wctrans_t', 'uint64_t', 'delete', 'and', 'register', 'false', 'int', - 'uintmax_t', 'off_t', 'char', 'int64_t', 'int_fast16_t', 'DIR', '_Atomic', - 'time_t', 'xor_eq', 'namespace', 'virtual', 'complex', 'bool', 'mutable', - 'if', 'int_least32_t', 'sig_atomic_t', 'and_eq', 'ssize_t', 'alignof', - '_Alignas', '_Generic', 'extern', 'class', 'typeid', 'short', 'for', - 'uint_fast64_t', 'wctype_t', 'explicit', 'or_eq', 'switch', 'uint32_t', - 'inline' ) - - assert_that( syntax_parse._KeywordsFromSyntaxListOutput( - ContentsOfTestFile( 'cpp_syntax' ) ), - contains_inanyorder( *expected_keywords ) ) - - -def KeywordsFromSyntaxListOutput_JavaSyntax_test(): - expected_keywords = ( - 'code', 'text', 'cols', 'datetime', 'disabled', 'shape', 'codetype', 'alt', - 'compact', 'style', 'valuetype', 'short', 'finally', 'continue', 'extends', - 'valign', 'bordercolor', 'do', 'return', 'rel', 'rules', 'void', - 'nohref', 'abbr', 'background', 'scrolling', 'instanceof', 'name', - 'summary', 'try', 'default', 'noshade', 'coords', 'dir', 'frame', 'usemap', - 'ismap', 'static', 'hspace', 'vlink', 'for', 'selected', 'rev', 'vspace', - 'content', 'method', 'version', 'volatile', 'above', 'new', 'charoff', - 'public', 'alink', 'enum', 'codebase', 'if', 'noresize', 'interface', - 'checked', 'byte', 'super', 'throw', 'src', 'language', 'package', - 'standby', 'script', 'longdesc', 'maxlength', 'cellpadding', 'throws', - 'tabindex', 'color', 'colspan', 'accesskey', 'float', 'while', 'private', - 'height', 'boolean', 'wrap', 'prompt', 'nowrap', 'size', 'rows', 'span', - 'clip', 'bgcolor', 'top', 'long', 'start', 'scope', 'scheme', 'type', - 'final', 'lang', 'visibility', 'else', 'assert', 'transient', 'link', - 'catch', 'true', 'serializable', 'target', 'lowsrc', 'this', 'double', - 'align', 'value', 'cite', 'headers', 'below', 'protected', 'declare', - 'classid', 'defer', 'false', 'synchronized', 'int', 'abstract', 'accept', - 'hreflang', 'char', 'border', 'id', 'native', 'rowspan', 'charset', - 'archive', 'strictfp', 'readonly', 'axis', 'cellspacing', 'profile', - 'multiple', 'object', 'action', 'pagex', 'pagey', 'marginheight', 'data', - 'class', 'frameborder', 'enctype', 'implements', 'break', 'gutter', 'url', - 'clear', 'face', 'switch', 'marginwidth', 'width', 'left' ) - - assert_that( syntax_parse._KeywordsFromSyntaxListOutput( - ContentsOfTestFile( 'java_syntax' ) ), - contains_inanyorder( *expected_keywords ) ) - - -def KeywordsFromSyntaxListOutput_PhpSyntax_ContainsFunctions_test(): - assert_that( syntax_parse._KeywordsFromSyntaxListOutput( - ContentsOfTestFile( 'php_syntax' ) ), - has_items( 'array_change_key_case' ) ) - - -def KeywordsFromSyntaxListOutput_PhpSyntax_ContainsPreProc_test(): - assert_that( syntax_parse._KeywordsFromSyntaxListOutput( - ContentsOfTestFile( 'php_syntax' ) ), - has_items( 'skip', 'function' ) ) - - -def KeywordsFromSyntaxListOutput_Basic_test(): - assert_that( syntax_parse._KeywordsFromSyntaxListOutput( """ +class SyntaxTest( TestCase ): + def test_KeywordsFromSyntaxListOutput_PythonSyntax( self ): + expected_keywords = ( 'bytearray', 'IndexError', 'all', 'help', 'vars', + 'SyntaxError', 'global', 'elif', 'unicode', 'sorted', 'memoryview', + 'isinstance', 'except', 'nonlocal', 'NameError', 'finally', + 'BytesWarning', 'dict', 'IOError', 'pass', 'oct', 'bin', 'SystemExit', + 'return', 'StandardError', 'format', 'TabError', 'break', 'next', + 'not', 'UnicodeDecodeError', 'False', 'RuntimeWarning', 'list', 'iter', + 'try', 'reload', 'Warning', 'round', 'dir', 'cmp', 'set', 'bytes', + 'UnicodeTranslateError', 'intern', 'issubclass', 'yield', 'Ellipsis', + 'hash', 'locals', 'BufferError', 'slice', 'for', 'FloatingPointError', + 'sum', 'VMSError', 'getattr', 'abs', 'print', 'import', 'True', + 'FutureWarning', 'ImportWarning', 'None', 'EOFError', 'len', + 'frozenset', 'ord', 'super', 'raise', 'TypeError', 'KeyboardInterrupt', + 'UserWarning', 'filter', 'range', 'staticmethod', 'SystemError', 'or', + 'BaseException', 'pow', 'RuntimeError', 'float', 'MemoryError', + 'StopIteration', 'globals', 'divmod', 'enumerate', 'apply', + 'LookupError', 'open', 'basestring', 'from', 'UnicodeError', 'zip', + 'hex', 'long', 'IndentationError', 'int', 'chr', '__import__', 'type', + 'Exception', 'continue', 'tuple', 'reduce', 'reversed', 'else', + 'assert', 'UnicodeEncodeError', 'input', 'with', 'hasattr', 'delattr', + 'setattr', 'raw_input', 'PendingDeprecationWarning', 'compile', + 'ArithmeticError', 'while', 'del', 'str', 'property', 'def', 'and', + 'GeneratorExit', 'ImportError', 'xrange', 'is', 'EnvironmentError', + 'KeyError', 'coerce', 'SyntaxWarning', 'file', 'in', 'unichr', 'ascii', + 'any', 'as', 'if', 'OSError', 'DeprecationWarning', 'min', + 'UnicodeWarning', 'execfile', 'id', 'complex', 'bool', 'ValueError', + 'NotImplemented', 'map', 'exec', 'buffer', 'max', 'class', 'object', + 'repr', 'callable', 'ZeroDivisionError', 'eval', '__debug__', + 'ReferenceError', 'AssertionError', 'classmethod', 'UnboundLocalError', + 'NotImplementedError', 'lambda', 'AttributeError', 'OverflowError', + 'WindowsError' ) + + assert_that( syntax_parse._KeywordsFromSyntaxListOutput( + ContentsOfTestFile( 'python_syntax' ) ), + contains_inanyorder( *expected_keywords ) ) + + + def test_KeywordsFromSyntaxListOutput_CppSyntax( self ): + expected_keywords = ( + 'int_fast32_t', 'FILE', 'size_t', 'bitor', 'typedef', 'const', 'struct', + 'uint8_t', 'fpos_t', 'thread_local', 'unsigned', 'uint_least16_t', 'do', + 'intptr_t', 'uint_least64_t', 'return', 'auto', 'void', '_Complex', + 'break', '_Alignof', 'not', 'using', '_Static_assert', '_Thread_local', + 'public', 'uint_fast16_t', 'this', 'continue', 'char32_t', 'int16_t', + 'intmax_t', 'static', 'clock_t', 'sizeof', 'int_fast64_t', 'mbstate_t', + 'try', 'xor', 'uint_fast32_t', 'int_least8_t', 'div_t', 'volatile', + 'template', 'char16_t', 'new', 'ldiv_t', 'int_least16_t', 'va_list', + 'uint_least8_t', 'goto', 'noreturn', 'enum', 'static_assert', 'bitand', + 'compl', 'imaginary', 'jmp_buf', 'throw', 'asm', 'ptrdiff_t', 'uint16_t', + 'or', 'uint_fast8_t', '_Bool', 'int32_t', 'float', 'private', 'restrict', + 'wint_t', 'operator', 'not_eq', '_Imaginary', 'alignas', 'union', 'long', + 'uint_least32_t', 'int_least64_t', 'friend', 'uintptr_t', 'int8_t', + 'else', 'export', 'int_fast8_t', 'catch', 'true', 'case', 'default', + 'double', '_Noreturn', 'signed', 'typename', 'while', 'protected', + 'wchar_t', 'wctrans_t', 'uint64_t', 'delete', 'and', 'register', 'false', + 'int', 'uintmax_t', 'off_t', 'char', 'int64_t', 'int_fast16_t', 'DIR', + '_Atomic', 'time_t', 'xor_eq', 'namespace', 'virtual', 'complex', 'bool', + 'mutable', 'if', 'int_least32_t', 'sig_atomic_t', 'and_eq', 'ssize_t', + 'alignof', '_Alignas', '_Generic', 'extern', 'class', 'typeid', 'short', + 'for', 'uint_fast64_t', 'wctype_t', 'explicit', 'or_eq', 'switch', + 'uint32_t', 'inline' ) + + assert_that( syntax_parse._KeywordsFromSyntaxListOutput( + ContentsOfTestFile( 'cpp_syntax' ) ), + contains_inanyorder( *expected_keywords ) ) + + + def test_KeywordsFromSyntaxListOutput_JavaSyntax( self ): + expected_keywords = ( + 'code', 'text', 'cols', 'datetime', 'disabled', 'shape', 'codetype', + 'alt', 'compact', 'style', 'valuetype', 'short', 'finally', 'continue', + 'extends', 'valign', 'bordercolor', 'do', 'return', 'rel', 'rules', + 'void', 'nohref', 'abbr', 'background', 'scrolling', 'instanceof', + 'name', 'summary', 'try', 'default', 'noshade', 'coords', 'dir', 'frame', + 'usemap', 'ismap', 'static', 'hspace', 'vlink', 'for', 'selected', 'rev', + 'vspace', 'content', 'method', 'version', 'volatile', 'above', 'new', + 'charoff', 'public', 'alink', 'enum', 'codebase', 'if', 'noresize', + 'interface', 'checked', 'byte', 'super', 'throw', 'src', 'language', + 'package', 'standby', 'script', 'longdesc', 'maxlength', 'cellpadding', + 'throws', 'tabindex', 'color', 'colspan', 'accesskey', 'float', 'while', + 'private', 'height', 'boolean', 'wrap', 'prompt', 'nowrap', 'size', + 'rows', 'span', 'clip', 'bgcolor', 'top', 'long', 'start', 'scope', + 'scheme', 'type', 'final', 'lang', 'visibility', 'else', 'assert', + 'transient', 'link', 'catch', 'true', 'serializable', 'target', 'lowsrc', + 'this', 'double', 'align', 'value', 'cite', 'headers', 'below', + 'protected', 'declare', 'classid', 'defer', 'false', 'synchronized', + 'int', 'abstract', 'accept', 'hreflang', 'char', 'border', 'id', + 'native', 'rowspan', 'charset', 'archive', 'strictfp', 'readonly', + 'axis', 'cellspacing', 'profile', 'multiple', 'object', 'action', + 'pagex', 'pagey', 'marginheight', 'data', 'class', 'frameborder', + 'enctype', 'implements', 'break', 'gutter', 'url', 'clear', 'face', + 'switch', 'marginwidth', 'width', 'left' ) + + assert_that( syntax_parse._KeywordsFromSyntaxListOutput( + ContentsOfTestFile( 'java_syntax' ) ), + contains_inanyorder( *expected_keywords ) ) + + + def test_KeywordsFromSyntaxListOutput_PhpSyntax_ContainsFunctions( self ): + assert_that( syntax_parse._KeywordsFromSyntaxListOutput( + ContentsOfTestFile( 'php_syntax' ) ), + has_items( 'array_change_key_case' ) ) + + + def test_KeywordsFromSyntaxListOutput_PhpSyntax_ContainsPreProc( self ): + assert_that( syntax_parse._KeywordsFromSyntaxListOutput( + ContentsOfTestFile( 'php_syntax' ) ), + has_items( 'skip', 'function' ) ) + + + def test_KeywordsFromSyntaxListOutput_Basic( self ): + assert_that( syntax_parse._KeywordsFromSyntaxListOutput( """ foogroup xxx foo bar zoo goo links to Statement""" ), - contains_inanyorder( 'foo', 'bar', 'zoo', 'goo' ) ) + contains_inanyorder( 'foo', 'bar', 'zoo', 'goo' ) ) -def KeywordsFromSyntaxListOutput_Function_test(): - assert_that( syntax_parse._KeywordsFromSyntaxListOutput( """ + def test_KeywordsFromSyntaxListOutput_Function( self ): + assert_that( syntax_parse._KeywordsFromSyntaxListOutput( """ foogroup xxx foo bar zoo goo links to Function""" ), - contains_inanyorder( 'foo', 'bar', 'zoo', 'goo' ) ) + contains_inanyorder( 'foo', 'bar', 'zoo', 'goo' ) ) -def KeywordsFromSyntaxListOutput_ContainedArgAllowed_test(): - assert_that( syntax_parse._KeywordsFromSyntaxListOutput( """ + def test_KeywordsFromSyntaxListOutput_ContainedArgAllowed( self ): + assert_that( syntax_parse._KeywordsFromSyntaxListOutput( """ phpFunctions xxx contained gzclose yaz_syntax html_entity_decode fbsql_read_blob png2wbmp mssql_init cpdf_set_title gztell fbsql_insert_id empty cpdf_restore mysql_field_type closelog swftext ldap_search curl_errno gmp_div_r mssql_data_seek getmyinode printer_draw_pie mcve_initconn ncurses_getmaxyx defined contained replace_child has_attributes specified insertdocument assign node_name hwstat addshape get_attribute_node html_dump_mem userlist links to Function""" ), # noqa - has_items( 'gzclose', 'userlist', 'ldap_search' ) ) + has_items( 'gzclose', 'userlist', 'ldap_search' ) ) -def KeywordsFromSyntaxListOutput_JunkIgnored_test(): - assert_that( syntax_parse._KeywordsFromSyntaxListOutput( """ + def test_KeywordsFromSyntaxListOutput_JunkIgnored( self ): + assert_that( syntax_parse._KeywordsFromSyntaxListOutput( """ --- Syntax items --- foogroup xxx foo bar zoo goo links to Statement Spell cluster=NONE NoSpell cluster=NONE""" ), - contains_inanyorder( 'foo', 'bar', 'zoo', 'goo' ) ) + contains_inanyorder( 'foo', 'bar', 'zoo', 'goo' ) ) -def KeywordsFromSyntaxListOutput_MultipleStatementGroups_test(): - assert_that( syntax_parse._KeywordsFromSyntaxListOutput( """ + def test_KeywordsFromSyntaxListOutput_MultipleStatementGroups( self ): + assert_that( syntax_parse._KeywordsFromSyntaxListOutput( """ foogroup xxx foo bar links to Statement bargroup xxx zoo goo links to Statement""" ), - contains_inanyorder( 'foo', 'bar', 'zoo', 'goo' ) ) + contains_inanyorder( 'foo', 'bar', 'zoo', 'goo' ) ) -def KeywordsFromSyntaxListOutput_StatementAndTypeGroups_test(): - assert_that( syntax_parse._KeywordsFromSyntaxListOutput( """ + def test_KeywordsFromSyntaxListOutput_StatementAndTypeGroups( self ): + assert_that( syntax_parse._KeywordsFromSyntaxListOutput( """ foogroup xxx foo bar links to Statement bargroup xxx zoo goo links to Type""" ), - contains_inanyorder( 'foo', 'bar', 'zoo', 'goo' ) ) + contains_inanyorder( 'foo', 'bar', 'zoo', 'goo' ) ) -def KeywordsFromSyntaxListOutput_StatementHierarchy_test(): - assert_that( syntax_parse._KeywordsFromSyntaxListOutput( """ + def test_KeywordsFromSyntaxListOutput_StatementHierarchy( self ): + assert_that( syntax_parse._KeywordsFromSyntaxListOutput( """ baa xxx foo bar links to Foo Foo xxx zoo goo links to Bar Bar xxx qux moo links to Statement""" ), - contains_inanyorder( 'foo', 'bar', 'zoo', 'goo', 'qux', 'moo' ) ) + contains_inanyorder( 'foo', 'bar', 'zoo', + 'goo', 'qux', 'moo' ) ) -def KeywordsFromSyntaxListOutput_TypeHierarchy_test(): - assert_that( syntax_parse._KeywordsFromSyntaxListOutput( """ + def test_KeywordsFromSyntaxListOutput_TypeHierarchy( self ): + assert_that( syntax_parse._KeywordsFromSyntaxListOutput( """ baa xxx foo bar links to Foo Foo xxx zoo goo links to Bar Bar xxx qux moo links to Type""" ), - contains_inanyorder( 'foo', 'bar', 'zoo', 'goo', 'qux', 'moo' ) ) + contains_inanyorder( 'foo', 'bar', 'zoo', + 'goo', 'qux', 'moo' ) ) -def KeywordsFromSyntaxListOutput_StatementAndTypeHierarchy_test(): - assert_that( syntax_parse._KeywordsFromSyntaxListOutput( """ + def test_KeywordsFromSyntaxListOutput_StatementAndTypeHierarchy( self ): + assert_that( syntax_parse._KeywordsFromSyntaxListOutput( """ tBaa xxx foo bar links to tFoo tFoo xxx zoo goo @@ -228,97 +234,100 @@ def KeywordsFromSyntaxListOutput_StatementAndTypeHierarchy_test(): links to sBar sBar xxx qux nc links to Statement""" ), - contains_inanyorder( 'foo', 'bar', 'zoo', 'goo', 'qux', 'moo', - 'na', 'nb', 'nc' ) ) + contains_inanyorder( 'foo', 'bar', 'zoo', 'goo', 'qux', 'moo', + 'na', 'nb', 'nc' ) ) -def SyntaxGroupsFromOutput_Basic_test(): - assert_that( syntax_parse._SyntaxGroupsFromOutput( """ + def test_SyntaxGroupsFromOutput_Basic( self ): + assert_that( syntax_parse._SyntaxGroupsFromOutput( """ foogroup xxx foo bar zoo goo links to Statement""" ), - has_item( 'foogroup' ) ) - - -def ExtractKeywordsFromGroup_Basic_test(): - assert_that( syntax_parse._ExtractKeywordsFromGroup( - syntax_parse.SyntaxGroup( '', [ - 'foo bar', - 'zoo goo', - ] ) ), - contains_inanyorder( 'foo', 'bar', 'zoo', 'goo' ) ) - - -def ExtractKeywordsFromGroup_Commas_test(): - assert_that( syntax_parse._ExtractKeywordsFromGroup( - syntax_parse.SyntaxGroup( '', [ - 'foo, bar,', - 'zoo goo', - ] ) ), - contains_inanyorder( 'foo', 'bar', 'zoo', 'goo' ) ) - - -def ExtractKeywordsFromGroup_WithLinksTo_test(): - assert_that( syntax_parse._ExtractKeywordsFromGroup( - syntax_parse.SyntaxGroup( '', [ - 'foo bar', - 'zoo goo', - 'links to Statement' - ] ) ), - contains_inanyorder( 'foo', 'bar', 'zoo', 'goo' ) ) - - -def ExtractKeywordsFromGroup_KeywordStarts_test(): - assert_that( syntax_parse._ExtractKeywordsFromGroup( - syntax_parse.SyntaxGroup( '', [ - 'foo bar', - 'contained boo baa', - 'zoo goo', - ] ) ), - contains_inanyorder( 'foo', 'bar', 'boo', 'baa', 'zoo', 'goo' ) ) - - -def ExtractKeywordsFromGroup_KeywordMiddle_test(): - assert_that( syntax_parse._ExtractKeywordsFromGroup( - syntax_parse.SyntaxGroup( '', [ - 'foo contained bar', - 'zoo goo' - ] ) ), - contains_inanyorder( 'foo', 'contained', 'bar', 'zoo', 'goo' ) ) - - -def ExtractKeywordsFromGroup_KeywordAssign_test(): - assert_that( syntax_parse._ExtractKeywordsFromGroup( - syntax_parse.SyntaxGroup( '', [ - 'nextgroup=zoo skipwhite foo bar', - 'zoo goo', - ] ) ), - contains_inanyorder( 'foo', 'bar', 'zoo', 'goo' ) ) - - -def ExtractKeywordsFromGroup_KeywordAssignAndMiddle_test(): - assert_that( syntax_parse._ExtractKeywordsFromGroup( - syntax_parse.SyntaxGroup( '', [ - 'nextgroup=zoo foo skipnl bar', - 'zoo goo', - ] ) ), - contains_inanyorder( 'foo', 'skipnl', 'bar', 'zoo', 'goo' ) ) - - -def ExtractKeywordsFromGroup_KeywordWithoutNextgroup_test(): - assert_that( syntax_parse._ExtractKeywordsFromGroup( - syntax_parse.SyntaxGroup( '', [ - 'skipempty foo bar', - 'zoo goo', - ] ) ), - contains_inanyorder( 'skipempty', 'foo', 'bar', 'zoo', 'goo' ) ) - - -def ExtractKeywordsFromGroup_ContainedSyntaxArgAllowed_test(): - assert_that( syntax_parse._ExtractKeywordsFromGroup( - syntax_parse.SyntaxGroup( '', [ - 'contained foo zoq', - 'contained bar goo', - 'far' - ] ) ), - contains_inanyorder( 'foo', 'zoq', 'bar', 'goo', 'far' ) ) + has_item( 'foogroup' ) ) + + + def test_ExtractKeywordsFromGroup_Basic( self ): + assert_that( syntax_parse._ExtractKeywordsFromGroup( + syntax_parse.SyntaxGroup( '', [ + 'foo bar', + 'zoo goo', + ] ) ), + contains_inanyorder( 'foo', 'bar', 'zoo', 'goo' ) ) + + + def test_ExtractKeywordsFromGroup_Commas( self ): + assert_that( syntax_parse._ExtractKeywordsFromGroup( + syntax_parse.SyntaxGroup( '', [ + 'foo, bar,', + 'zoo goo', + ] ) ), + contains_inanyorder( 'foo', 'bar', 'zoo', 'goo' ) ) + + + def test_ExtractKeywordsFromGroup_WithLinksTo( self ): + assert_that( syntax_parse._ExtractKeywordsFromGroup( + syntax_parse.SyntaxGroup( '', [ + 'foo bar', + 'zoo goo', + 'links to Statement' + ] ) ), + contains_inanyorder( 'foo', 'bar', 'zoo', 'goo' ) ) + + + def test_ExtractKeywordsFromGroup_KeywordStarts( self ): + assert_that( syntax_parse._ExtractKeywordsFromGroup( + syntax_parse.SyntaxGroup( '', [ + 'foo bar', + 'contained boo baa', + 'zoo goo', + ] ) ), + contains_inanyorder( 'foo', 'bar', 'boo', + 'baa', 'zoo', 'goo' ) ) + + + def test_ExtractKeywordsFromGroup_KeywordMiddle( self ): + assert_that( syntax_parse._ExtractKeywordsFromGroup( + syntax_parse.SyntaxGroup( '', [ + 'foo contained bar', + 'zoo goo' + ] ) ), + contains_inanyorder( 'foo', 'contained', + 'bar', 'zoo', 'goo' ) ) + + + def test_ExtractKeywordsFromGroup_KeywordAssign( self ): + assert_that( syntax_parse._ExtractKeywordsFromGroup( + syntax_parse.SyntaxGroup( '', [ + 'nextgroup=zoo skipwhite foo bar', + 'zoo goo', + ] ) ), + contains_inanyorder( 'foo', 'bar', 'zoo', 'goo' ) ) + + + def test_ExtractKeywordsFromGroup_KeywordAssignAndMiddle( self ): + assert_that( syntax_parse._ExtractKeywordsFromGroup( + syntax_parse.SyntaxGroup( '', [ + 'nextgroup=zoo foo skipnl bar', + 'zoo goo', + ] ) ), + contains_inanyorder( 'foo', 'skipnl', 'bar', 'zoo', 'goo' ) ) + + + def test_ExtractKeywordsFromGroup_KeywordWithoutNextgroup( self ): + assert_that( syntax_parse._ExtractKeywordsFromGroup( + syntax_parse.SyntaxGroup( '', [ + 'skipempty foo bar', + 'zoo goo', + ] ) ), + contains_inanyorder( 'skipempty', 'foo', + 'bar', 'zoo', 'goo' ) ) + + + def test_ExtractKeywordsFromGroup_ContainedSyntaxArgAllowed( self ): + assert_that( syntax_parse._ExtractKeywordsFromGroup( + syntax_parse.SyntaxGroup( '', [ + 'contained foo zoq', + 'contained bar goo', + 'far' + ] ) ), + contains_inanyorder( 'foo', 'zoq', 'bar', 'goo', 'far' ) ) diff --git a/python/ycm/tests/test_utils.py b/python/ycm/tests/test_utils.py index d6cdd7d79c..86b75c8834 100644 --- a/python/ycm/tests/test_utils.py +++ b/python/ycm/tests/test_utils.py @@ -17,11 +17,11 @@ from collections import defaultdict, namedtuple from unittest.mock import DEFAULT, MagicMock, patch +from unittest import skip from hamcrest import assert_that, equal_to import contextlib import functools import json -import pytest import os import re import sys @@ -712,7 +712,7 @@ def Wrapper( *args, **kwargs ): raise test_exception # Failed for the right reason - pytest.skip( reason ) + skip( reason ) else: raise AssertionError( f'Test was expected to fail: { reason }' ) return Wrapper diff --git a/python/ycm/tests/vimsupport_test.py b/python/ycm/tests/vimsupport_test.py index 1e25ae03d3..1b2d890b58 100644 --- a/python/ycm/tests/vimsupport_test.py +++ b/python/ycm/tests/vimsupport_test.py @@ -23,1935 +23,1885 @@ from ycm import vimsupport from hamcrest import ( assert_that, calling, contains_exactly, empty, equal_to, has_entry, raises ) +from unittest import TestCase from unittest.mock import MagicMock, call, patch from ycmd.utils import ToBytes import os import json -@patch( 'vim.eval', new_callable = ExtendedMock ) -def SetLocationListsForBuffer_Current_test( vim_eval ): - diagnostics = [ { - 'bufnr': 3, - 'filename': 'some_filename', - 'lnum': 5, - 'col': 22, - 'type': 'E', - 'valid': 1 - } ] - current_buffer = VimBuffer( '/test', number = 3 ) - with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): - vimsupport.SetLocationListsForBuffer( 3, diagnostics ) - - vim_eval.assert_has_exact_calls( [ - call( f'setloclist( 1, { json.dumps( diagnostics ) } )' ) - ] ) - - -@patch( 'vim.eval', new_callable = ExtendedMock ) -def SetLocationListsForBuffer_NotCurrent_test( vim_eval ): - diagnostics = [ { - 'bufnr': 3, - 'filename': 'some_filename', - 'lnum': 5, - 'col': 22, - 'type': 'E', - 'valid': 1 - } ] - current_buffer = VimBuffer( '/test', number = 3 ) - other_buffer = VimBuffer( '/notcurrent', number = 1 ) - with MockVimBuffers( [ current_buffer, other_buffer ], [ current_buffer ] ): - vimsupport.SetLocationListsForBuffer( 1, diagnostics ) - - vim_eval.assert_not_called() - - -@patch( 'vim.eval', new_callable = ExtendedMock, side_effect = [ -1, 1 ] ) -def SetLocationListsForBuffer_NotVisible_test( vim_eval ): - diagnostics = [ { - 'bufnr': 3, - 'filename': 'some_filename', - 'lnum': 5, - 'col': 22, - 'type': 'E', - 'valid': 1 - } ] - current_buffer = VimBuffer( '/test', number = 3 ) - other_buffer = VimBuffer( '/notcurrent', number = 1 ) - with MockVimBuffers( [ current_buffer, other_buffer ], [ current_buffer ] ): - vimsupport.SetLocationListsForBuffer( 1, diagnostics ) - - vim_eval.assert_not_called() - - -@patch( 'vim.eval', new_callable = ExtendedMock, side_effect = [ -1, 1 ] ) -def SetLocationListsForBuffer_MultipleWindows_test( vim_eval ): - diagnostics = [ { - 'bufnr': 3, - 'filename': 'some_filename', - 'lnum': 5, - 'col': 22, - 'type': 'E', - 'valid': 1 - } ] - current_buffer = VimBuffer( '/test', number = 3 ) - other_buffer = VimBuffer( '/notcurrent', number = 1 ) - with MockVimBuffers( [ current_buffer, other_buffer ], - [ current_buffer, other_buffer ] ): - vimsupport.SetLocationListsForBuffer( 1, diagnostics ) - - vim_eval.assert_has_exact_calls( [ - call( f'setloclist( 2, { json.dumps( diagnostics ) } )' ) - ] ) - - -@patch( 'vim.eval', new_callable = ExtendedMock ) -def SetLocationList_test( vim_eval ): - diagnostics = [ { - 'bufnr': 3, - 'filename': 'some_filename', - 'lnum': 5, - 'col': 22, - 'type': 'E', - 'valid': 1 - } ] - current_buffer = VimBuffer( '/test', number = 3 ) - with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 1 ) ): - vimsupport.SetLocationList( diagnostics ) - - vim_eval.assert_has_calls( [ - call( f'setloclist( 0, { json.dumps( diagnostics ) } )' ) - ] ) - - -@patch( 'vim.eval', new_callable = ExtendedMock ) -def SetLocationList_NotCurrent_test( vim_eval ): - diagnostics = [ { - 'bufnr': 3, - 'filename': 'some_filename', - 'lnum': 5, - 'col': 22, - 'type': 'E', - 'valid': 1 - } ] - current_buffer = VimBuffer( '/test', number = 3 ) - other_buffer = VimBuffer( '/notcurrent', number = 1 ) - with MockVimBuffers( [ current_buffer, other_buffer ], - [ current_buffer, other_buffer ], - ( 1, 1 ) ): - vimsupport.SetLocationList( diagnostics ) - - # This version does not check the current buffer and just sets the current win - vim_eval.assert_has_exact_calls( [ - call( f'setloclist( 0, { json.dumps( diagnostics ) } )' ) - ] ) - - -@patch( 'ycm.vimsupport.VariableExists', return_value = True ) -@patch( 'ycm.vimsupport.SetFittingHeightForCurrentWindow' ) -@patch( 'vim.command', new_callable = ExtendedMock ) -def OpenLocationList_test( vim_command, fitting_height, variable_exists ): - vimsupport.OpenLocationList( focus = False, autoclose = True ) - vim_command.assert_has_exact_calls( [ - call( 'lopen' ), - call( 'augroup ycmlocation' ), - call( 'autocmd! * ' ), - call( 'autocmd WinLeave ' - 'if bufnr( "%" ) == expand( "" ) | q | endif ' - '| autocmd! ycmlocation' ), - call( 'augroup END' ), - call( 'doautocmd User YcmLocationOpened' ), - call( 'silent! wincmd p' ) - ] ) - fitting_height.assert_called_once_with() - variable_exists.assert_called_once_with( '#User#YcmLocationOpened' ) - - -@patch( 'vim.command' ) -def SetFittingHeightForCurrentWindow_LineWrapOn_test( vim_command, *args ): - # Create a two lines buffer whose first line is longer than the window width. - current_buffer = VimBuffer( 'buffer', - contents = [ 'a' * 140, 'b' * 80 ] ) - with MockVimBuffers( [ current_buffer ], [ current_buffer ] ) as vim: - vim.current.window.width = 120 - vim.current.window.options[ 'wrap' ] = True - vimsupport.SetFittingHeightForCurrentWindow() - vim_command.assert_called_once_with( '3wincmd _' ) - - -@patch( 'vim.command' ) -def SetFittingHeightForCurrentWindow_NoResize_test( vim_command, *args ): - # Create a two lines buffer whose first line is longer than the window width. - current_buffer = VimBuffer( 'buffer', - contents = [ 'a' * 140, 'b' * 80 ], - vars = { 'ycm_no_resize': 1 } ) - with MockVimBuffers( [ current_buffer ], [ current_buffer ] ) as vim: - vim.current.window.width = 120 - vim.current.window.options[ 'wrap' ] = True - vimsupport.SetFittingHeightForCurrentWindow() - vim_command.assert_not_called() - - -@patch( 'vim.command' ) -def SetFittingHeightForCurrentWindow_LineWrapOff_test( vim_command, *args ): - # Create a two lines buffer whose first line is longer than the window width. - current_buffer = VimBuffer( 'buffer', - contents = [ 'a' * 140, 'b' * 80 ] ) - with MockVimBuffers( [ current_buffer ], [ current_buffer ] ) as vim: - vim.current.window.width = 120 - vim.current.window.options[ 'wrap' ] = False - vimsupport.SetFittingHeightForCurrentWindow() - vim_command.assert_called_once_with( '2wincmd _' ) - - def AssertBuffersAreEqualAsBytes( result_buffer, expected_buffer ): assert_that( len( result_buffer ), equal_to( len( expected_buffer ) ) ) for result_line, expected_line in zip( result_buffer, expected_buffer ): assert_that( ToBytes( result_line ), equal_to( ToBytes( expected_line ) ) ) -@patch( 'vim.current.window.cursor', ( 1, 1 ) ) -def ReplaceChunk_SingleLine_Repl_1_test(): - # Replace with longer range - result_buffer = VimBuffer( 'buffer', contents = [ 'This is a string' ] ) - start, end = _BuildLocations( 1, 11, 1, 17 ) - vimsupport.ReplaceChunk( start, end, 'pie', result_buffer ) +def _BuildChunk( start_line, + start_column, + end_line, + end_column, + replacement_text, filepath='test_file_name' ): + return { + 'range': { + 'start': { + 'filepath': filepath, + 'line_num': start_line, + 'column_num': start_column, + }, + 'end': { + 'filepath': filepath, + 'line_num': end_line, + 'column_num': end_column, + }, + }, + 'replacement_text': replacement_text + } - AssertBuffersAreEqualAsBytes( [ 'This is a pie' ], result_buffer ) - # and replace again - start, end = _BuildLocations( 1, 10, 1, 11 ) - vimsupport.ReplaceChunk( start, end, ' piece of ', result_buffer ) +def _BuildLocations( start_line, start_column, end_line, end_column ): + return { + 'line_num' : start_line, + 'column_num': start_column, + }, { + 'line_num' : end_line, + 'column_num': end_column, + } - AssertBuffersAreEqualAsBytes( [ 'This is a piece of pie' ], result_buffer ) - # and once more, for luck - start, end = _BuildLocations( 1, 1, 1, 5 ) - vimsupport.ReplaceChunk( start, end, 'How long', result_buffer ) +class VimsupportTest( TestCase ): + @patch( 'vim.eval', new_callable = ExtendedMock ) + def test_SetLocationListsForBuffer_Current( self, vim_eval ): + diagnostics = [ { + 'bufnr': 3, + 'filename': 'some_filename', + 'lnum': 5, + 'col': 22, + 'type': 'E', + 'valid': 1 + } ] + current_buffer = VimBuffer( '/test', number = 3 ) + with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): + vimsupport.SetLocationListsForBuffer( 3, diagnostics ) + + vim_eval.assert_has_exact_calls( [ + call( f'setloclist( 1, { json.dumps( diagnostics ) } )' ) + ] ) - AssertBuffersAreEqualAsBytes( [ 'How long is a piece of pie' ], - result_buffer ) + @patch( 'vim.eval', new_callable = ExtendedMock ) + def test_SetLocationListsForBuffer_NotCurrent( self, vim_eval ): + diagnostics = [ { + 'bufnr': 3, + 'filename': 'some_filename', + 'lnum': 5, + 'col': 22, + 'type': 'E', + 'valid': 1 + } ] + current_buffer = VimBuffer( '/test', number = 3 ) + other_buffer = VimBuffer( '/notcurrent', number = 1 ) + with MockVimBuffers( [ current_buffer, other_buffer ], [ current_buffer ] ): + vimsupport.SetLocationListsForBuffer( 1, diagnostics ) + + vim_eval.assert_not_called() + + + @patch( 'vim.eval', new_callable = ExtendedMock, side_effect = [ -1, 1 ] ) + def test_SetLocationListsForBuffer_NotVisible( self, vim_eval ): + diagnostics = [ { + 'bufnr': 3, + 'filename': 'some_filename', + 'lnum': 5, + 'col': 22, + 'type': 'E', + 'valid': 1 + } ] + current_buffer = VimBuffer( '/test', number = 3 ) + other_buffer = VimBuffer( '/notcurrent', number = 1 ) + with MockVimBuffers( [ current_buffer, other_buffer ], [ current_buffer ] ): + vimsupport.SetLocationListsForBuffer( 1, diagnostics ) + + vim_eval.assert_not_called() + + + @patch( 'vim.eval', new_callable = ExtendedMock, side_effect = [ -1, 1 ] ) + def test_SetLocationListsForBuffer_MultipleWindows( self, vim_eval ): + diagnostics = [ { + 'bufnr': 3, + 'filename': 'some_filename', + 'lnum': 5, + 'col': 22, + 'type': 'E', + 'valid': 1 + } ] + current_buffer = VimBuffer( '/test', number = 3 ) + other_buffer = VimBuffer( '/notcurrent', number = 1 ) + with MockVimBuffers( [ current_buffer, other_buffer ], + [ current_buffer, other_buffer ] ): + vimsupport.SetLocationListsForBuffer( 1, diagnostics ) + + vim_eval.assert_has_exact_calls( [ + call( f'setloclist( 2, { json.dumps( diagnostics ) } )' ) + ] ) -@patch( 'vim.current.window.cursor', ( 1, 1 ) ) -def ReplaceChunk_SingleLine_Repl_2_test(): - # Replace with shorter range - result_buffer = VimBuffer( 'buffer', contents = [ 'This is a string' ] ) - start, end = _BuildLocations( 1, 11, 1, 17 ) - vimsupport.ReplaceChunk( start, end, 'test', result_buffer ) - AssertBuffersAreEqualAsBytes( [ 'This is a test' ], result_buffer ) + @patch( 'vim.eval', new_callable = ExtendedMock ) + def test_SetLocationList( self, vim_eval ): + diagnostics = [ { + 'bufnr': 3, + 'filename': 'some_filename', + 'lnum': 5, + 'col': 22, + 'type': 'E', + 'valid': 1 + } ] + current_buffer = VimBuffer( '/test', number = 3 ) + with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 1 ) ): + vimsupport.SetLocationList( diagnostics ) + + vim_eval.assert_has_calls( [ + call( f'setloclist( 0, { json.dumps( diagnostics ) } )' ) + ] ) -@patch( 'vim.current.window.cursor', ( 1, 1 ) ) -def ReplaceChunk_SingleLine_Repl_3_test(): - # Replace with equal range - result_buffer = VimBuffer( 'buffer', contents = [ 'This is a string' ] ) - start, end = _BuildLocations( 1, 6, 1, 8 ) - vimsupport.ReplaceChunk( start, end, 'be', result_buffer ) + @patch( 'vim.eval', new_callable = ExtendedMock ) + def test_SetLocationList_NotCurrent( self, vim_eval ): + diagnostics = [ { + 'bufnr': 3, + 'filename': 'some_filename', + 'lnum': 5, + 'col': 22, + 'type': 'E', + 'valid': 1 + } ] + current_buffer = VimBuffer( '/test', number = 3 ) + other_buffer = VimBuffer( '/notcurrent', number = 1 ) + with MockVimBuffers( [ current_buffer, other_buffer ], + [ current_buffer, other_buffer ], + ( 1, 1 ) ): + vimsupport.SetLocationList( diagnostics ) + + # This version does not check the current + # buffer and just sets the current win + vim_eval.assert_has_exact_calls( [ + call( f'setloclist( 0, { json.dumps( diagnostics ) } )' ) + ] ) - AssertBuffersAreEqualAsBytes( [ 'This be a string' ], result_buffer ) + @patch( 'ycm.vimsupport.VariableExists', return_value = True ) + @patch( 'ycm.vimsupport.SetFittingHeightForCurrentWindow' ) + @patch( 'vim.command', new_callable = ExtendedMock ) + def test_OpenLocationList( + self, vim_command, fitting_height, variable_exists ): + vimsupport.OpenLocationList( focus = False, autoclose = True ) + vim_command.assert_has_exact_calls( [ + call( 'lopen' ), + call( 'augroup ycmlocation' ), + call( 'autocmd! * ' ), + call( 'autocmd WinLeave ' + 'if bufnr( "%" ) == expand( "" ) | q | endif ' + '| autocmd! ycmlocation' ), + call( 'augroup END' ), + call( 'doautocmd User YcmLocationOpened' ), + call( 'silent! wincmd p' ) + ] ) + fitting_height.assert_called_once_with() + variable_exists.assert_called_once_with( '#User#YcmLocationOpened' ) + + + @patch( 'vim.command' ) + def test_SetFittingHeightForCurrentWindow_LineWrapOn( + self, vim_command, *args ): + # Create a two lines buffer whose first + # line is longer than the window width. + current_buffer = VimBuffer( 'buffer', + contents = [ 'a' * 140, 'b' * 80 ] ) + with MockVimBuffers( [ current_buffer ], [ current_buffer ] ) as vim: + vim.current.window.width = 120 + vim.current.window.options[ 'wrap' ] = True + vimsupport.SetFittingHeightForCurrentWindow() + vim_command.assert_called_once_with( '3wincmd _' ) + + + @patch( 'vim.command' ) + def test_SetFittingHeightForCurrentWindow_NoResize( + self, vim_command, *args ): + # Create a two lines buffer whose first + # line is longer than the window width. + current_buffer = VimBuffer( 'buffer', + contents = [ 'a' * 140, 'b' * 80 ], + vars = { 'ycm_no_resize': 1 } ) + with MockVimBuffers( [ current_buffer ], [ current_buffer ] ) as vim: + vim.current.window.width = 120 + vim.current.window.options[ 'wrap' ] = True + vimsupport.SetFittingHeightForCurrentWindow() + vim_command.assert_not_called() -@patch( 'vim.current.window.cursor', ( 1, 1 ) ) -def ReplaceChunk_SingleLine_Add_1_test(): - # Insert at start - result_buffer = VimBuffer( 'buffer', contents = [ 'is a string' ] ) - start, end = _BuildLocations( 1, 1, 1, 1 ) - vimsupport.ReplaceChunk( start, end, 'This ', result_buffer ) - AssertBuffersAreEqualAsBytes( [ 'This is a string' ], result_buffer ) + @patch( 'vim.command' ) + def test_SetFittingHeightForCurrentWindow_LineWrapOff( + self, vim_command, *args ): + # Create a two lines buffer whose first + # line is longer than the window width. + current_buffer = VimBuffer( 'buffer', + contents = [ 'a' * 140, 'b' * 80 ] ) + with MockVimBuffers( [ current_buffer ], [ current_buffer ] ) as vim: + vim.current.window.width = 120 + vim.current.window.options[ 'wrap' ] = False + vimsupport.SetFittingHeightForCurrentWindow() + vim_command.assert_called_once_with( '2wincmd _' ) -@patch( 'vim.current.window.cursor', ( 1, 1 ) ) -def ReplaceChunk_SingleLine_Add_2_test(): - # Insert at end - result_buffer = VimBuffer( 'buffer', contents = [ 'This is a ' ] ) - start, end = _BuildLocations( 1, 11, 1, 11 ) - vimsupport.ReplaceChunk( start, end, 'string', result_buffer ) + @patch( 'vim.current.window.cursor', ( 1, 1 ) ) + def test_ReplaceChunk_SingleLine_Repl_1( self ): + # Replace with longer range + result_buffer = VimBuffer( 'buffer', contents = [ 'This is a string' ] ) + start, end = _BuildLocations( 1, 11, 1, 17 ) + vimsupport.ReplaceChunk( start, end, 'pie', result_buffer ) - AssertBuffersAreEqualAsBytes( [ 'This is a string' ], result_buffer ) + AssertBuffersAreEqualAsBytes( [ 'This is a pie' ], result_buffer ) + # and replace again + start, end = _BuildLocations( 1, 10, 1, 11 ) + vimsupport.ReplaceChunk( start, end, ' piece of ', result_buffer ) -@patch( 'vim.current.window.cursor', ( 1, 1 ) ) -def ReplaceChunk_SingleLine_Add_3_test(): - # Insert in the middle - result_buffer = VimBuffer( 'buffer', contents = [ 'This is a string' ] ) - start, end = _BuildLocations( 1, 8, 1, 8 ) - vimsupport.ReplaceChunk( start, end, ' not', result_buffer ) + AssertBuffersAreEqualAsBytes( [ 'This is a piece of pie' ], result_buffer ) - AssertBuffersAreEqualAsBytes( [ 'This is not a string' ], result_buffer ) + # and once more, for luck + start, end = _BuildLocations( 1, 1, 1, 5 ) + vimsupport.ReplaceChunk( start, end, 'How long', result_buffer ) + AssertBuffersAreEqualAsBytes( [ 'How long is a piece of pie' ], + result_buffer ) -@patch( 'vim.current.window.cursor', ( 1, 1 ) ) -def ReplaceChunk_SingleLine_Del_1_test(): - # Delete from start - result_buffer = VimBuffer( 'buffer', contents = [ 'This is a string' ] ) - start, end = _BuildLocations( 1, 1, 1, 6 ) - vimsupport.ReplaceChunk( start, end, '', result_buffer ) - AssertBuffersAreEqualAsBytes( [ 'is a string' ], result_buffer ) + @patch( 'vim.current.window.cursor', ( 1, 1 ) ) + def test_ReplaceChunk_SingleLine_Repl_2( self ): + # Replace with shorter range + result_buffer = VimBuffer( 'buffer', contents = [ 'This is a string' ] ) + start, end = _BuildLocations( 1, 11, 1, 17 ) + vimsupport.ReplaceChunk( start, end, 'test', result_buffer ) + AssertBuffersAreEqualAsBytes( [ 'This is a test' ], result_buffer ) -@patch( 'vim.current.window.cursor', ( 1, 1 ) ) -def ReplaceChunk_SingleLine_Del_2_test(): - # Delete from end - result_buffer = VimBuffer( 'buffer', contents = [ 'This is a string' ] ) - start, end = _BuildLocations( 1, 10, 1, 18 ) - vimsupport.ReplaceChunk( start, end, '', result_buffer ) - AssertBuffersAreEqualAsBytes( [ 'This is a' ], result_buffer ) + @patch( 'vim.current.window.cursor', ( 1, 1 ) ) + def test_ReplaceChunk_SingleLine_Repl_3( self ): + # Replace with equal range + result_buffer = VimBuffer( 'buffer', contents = [ 'This is a string' ] ) + start, end = _BuildLocations( 1, 6, 1, 8 ) + vimsupport.ReplaceChunk( start, end, 'be', result_buffer ) + AssertBuffersAreEqualAsBytes( [ 'This be a string' ], result_buffer ) -@patch( 'vim.current.window.cursor', ( 1, 1 ) ) -def ReplaceChunk_SingleLine_Del_3_test(): - # Delete from middle - result_buffer = VimBuffer( 'buffer', contents = [ 'This is not a string' ] ) - start, end = _BuildLocations( 1, 9, 1, 13 ) - vimsupport.ReplaceChunk( start, end, '', result_buffer ) - AssertBuffersAreEqualAsBytes( [ 'This is a string' ], result_buffer ) + @patch( 'vim.current.window.cursor', ( 1, 1 ) ) + def test_ReplaceChunk_SingleLine_Add_1( self ): + # Insert at start + result_buffer = VimBuffer( 'buffer', contents = [ 'is a string' ] ) + start, end = _BuildLocations( 1, 1, 1, 1 ) + vimsupport.ReplaceChunk( start, end, 'This ', result_buffer ) + AssertBuffersAreEqualAsBytes( [ 'This is a string' ], result_buffer ) -@patch( 'vim.current.window.cursor', ( 1, 1 ) ) -def ReplaceChunk_SingleLine_Unicode_ReplaceUnicodeChars_test(): - # Replace Unicode characters. - result_buffer = VimBuffer( - 'buffer', contents = [ 'This Uniçø∂‰ string is in the middle' ] ) - start, end = _BuildLocations( 1, 6, 1, 20 ) - vimsupport.ReplaceChunk( start, end, 'Unicode ', result_buffer ) - AssertBuffersAreEqualAsBytes( [ 'This Unicode string is in the middle' ], - result_buffer ) + @patch( 'vim.current.window.cursor', ( 1, 1 ) ) + def test_ReplaceChunk_SingleLine_Add_2( self ): + # Insert at end + result_buffer = VimBuffer( 'buffer', contents = [ 'This is a ' ] ) + start, end = _BuildLocations( 1, 11, 1, 11 ) + vimsupport.ReplaceChunk( start, end, 'string', result_buffer ) + + AssertBuffersAreEqualAsBytes( [ 'This is a string' ], result_buffer ) -@patch( 'vim.current.window.cursor', ( 1, 1 ) ) -def ReplaceChunk_SingleLine_Unicode_ReplaceAfterUnicode_test(): - # Replace ASCII characters after Unicode characters in the line. - result_buffer = VimBuffer( - 'buffer', contents = [ 'This Uniçø∂‰ string is in the middle' ] ) - start, end = _BuildLocations( 1, 30, 1, 43 ) - vimsupport.ReplaceChunk( start, end, 'fåke', result_buffer ) + @patch( 'vim.current.window.cursor', ( 1, 1 ) ) + def test_ReplaceChunk_SingleLine_Add_3( self ): + # Insert in the middle + result_buffer = VimBuffer( 'buffer', contents = [ 'This is a string' ] ) + start, end = _BuildLocations( 1, 8, 1, 8 ) + vimsupport.ReplaceChunk( start, end, ' not', result_buffer ) - AssertBuffersAreEqualAsBytes( [ 'This Uniçø∂‰ string is fåke' ], - result_buffer ) + AssertBuffersAreEqualAsBytes( [ 'This is not a string' ], result_buffer ) -@patch( 'vim.current.window.cursor', ( 1, 1 ) ) -def ReplaceChunk_SingleLine_Unicode_Grown_test(): - # Replace ASCII characters after Unicode characters in the line. - result_buffer = VimBuffer( 'buffer', contents = [ 'a' ] ) - start, end = _BuildLocations( 1, 1, 1, 2 ) - vimsupport.ReplaceChunk( start, end, 'å', result_buffer ) - - AssertBuffersAreEqualAsBytes( [ 'å' ], result_buffer ) - - -@patch( 'vim.current.window.cursor', ( 1, 1 ) ) -def ReplaceChunk_RemoveSingleLine_test(): - result_buffer = VimBuffer( 'buffer', contents = [ 'aAa', - 'aBa', - 'aCa' ] ) - start, end = _BuildLocations( 2, 1, 3, 1 ) - vimsupport.ReplaceChunk( start, end, '', result_buffer ) - # First line is not affected. - AssertBuffersAreEqualAsBytes( [ 'aAa', - 'aCa' ], result_buffer ) - - -@patch( 'vim.current.window.cursor', ( 1, 1 ) ) -def ReplaceChunk_SingleToMultipleLines_test(): - result_buffer = VimBuffer( 'buffer', contents = [ 'aAa', - 'aBa', - 'aCa' ] ) - start, end = _BuildLocations( 2, 3, 2, 4 ) - vimsupport.ReplaceChunk( start, end, 'cccc', result_buffer ) - - AssertBuffersAreEqualAsBytes( [ 'aAa', - 'aBcccc', - 'aCa' ], result_buffer ) - - # now make another change to the second line - start, end = _BuildLocations( 2, 2, 2, 2 ) - vimsupport.ReplaceChunk( start, end, 'Eb\nbF', result_buffer ) - - AssertBuffersAreEqualAsBytes( [ 'aAa', - 'aEb', - 'bFBcccc', - 'aCa' ], result_buffer ) - - -@patch( 'vim.current.window.cursor', ( 1, 1 ) ) -def ReplaceChunk_SingleToMultipleLines2_test(): - result_buffer = VimBuffer( 'buffer', contents = [ 'aAa', - 'aBa', - 'aCa' ] ) - start, end = _BuildLocations( 2, 2, 2, 2 ) - vimsupport.ReplaceChunk( start, end, 'Eb\nbFb\nG', result_buffer ) - - AssertBuffersAreEqualAsBytes( [ 'aAa', - 'aEb', - 'bFb', - 'GBa', - 'aCa' ], result_buffer ) - - -@patch( 'vim.current.window.cursor', ( 1, 1 ) ) -def ReplaceChunk_SingleToMultipleLines3_test(): - result_buffer = VimBuffer( 'buffer', contents = [ 'aAa', - 'aBa', - 'aCa' ] ) - start, end = _BuildLocations( 2, 2, 2, 2 ) - vimsupport.ReplaceChunk( start, end, 'Eb\nbFb\nbGb', result_buffer ) - - AssertBuffersAreEqualAsBytes( [ 'aAa', - 'aEb', - 'bFb', - 'bGbBa', - 'aCa' ], result_buffer ) - - -@patch( 'vim.current.window.cursor', ( 1, 1 ) ) -def ReplaceChunk_SingleToMultipleLinesReplace_test(): - result_buffer = VimBuffer( 'buffer', contents = [ 'aAa', - 'aBa', - 'aCa' ] ) - start, end = _BuildLocations( 1, 2, 1, 4 ) - vimsupport.ReplaceChunk( start, end, 'Eb\nbFb\nbGb', result_buffer ) - - AssertBuffersAreEqualAsBytes( [ 'aEb', - 'bFb', - 'bGb', - 'aBa', - 'aCa' ], result_buffer ) - - -@patch( 'vim.current.window.cursor', ( 1, 1 ) ) -def ReplaceChunk_SingleToMultipleLinesReplace_2_test(): - result_buffer = VimBuffer( 'buffer', contents = [ 'aAa', - 'aBa', - 'aCa' ] ) - start, end = _BuildLocations( 1, 4, 1, 4 ) - vimsupport.ReplaceChunk( start, end, 'cccc', result_buffer ) - - AssertBuffersAreEqualAsBytes( [ 'aAacccc', - 'aBa', - 'aCa', ], result_buffer ) - - # now do a subsequent change (insert in the middle of the first line) - start, end = _BuildLocations( 1, 2, 1, 4 ) - vimsupport.ReplaceChunk( start, end, 'Eb\nbFb\nbGb', result_buffer ) - - AssertBuffersAreEqualAsBytes( [ 'aEb', - 'bFb', - 'bGbcccc', - 'aBa', - 'aCa' ], result_buffer ) - - -@patch( 'vim.current.window.cursor', ( 1, 1 ) ) -def ReplaceChunk_MultipleLinesToSingleLine_test(): - result_buffer = VimBuffer( 'buffer', contents = [ 'aAa', - 'aBa', - 'aCaaaa' ] ) - start, end = _BuildLocations( 3, 4, 3, 5 ) - vimsupport.ReplaceChunk( start, end, 'dd\ndd', result_buffer ) - - AssertBuffersAreEqualAsBytes( [ 'aAa', - 'aBa', - 'aCadd', - 'ddaa' ], result_buffer ) - - # make another modification applying offsets - start, end = _BuildLocations( 3, 3, 3, 4 ) - vimsupport.ReplaceChunk( start, end, 'cccc', result_buffer ) - - AssertBuffersAreEqualAsBytes( [ 'aAa', - 'aBa', - 'aCccccdd', - 'ddaa' ], result_buffer ) - - # and another, for luck - start, end = _BuildLocations( 2, 2, 3, 2 ) - vimsupport.ReplaceChunk( start, end, 'E', result_buffer ) - - AssertBuffersAreEqualAsBytes( [ 'aAa', - 'aECccccdd', - 'ddaa' ], result_buffer ) - - -@patch( 'vim.current.window.cursor', ( 1, 1 ) ) -def ReplaceChunk_MultipleLinesToSameMultipleLines_test(): - result_buffer = VimBuffer( 'buffer', contents = [ 'aAa', - 'aBa', - 'aCa', - 'aDe' ] ) - start, end = _BuildLocations( 2, 2, 3, 2 ) - vimsupport.ReplaceChunk( start, end, 'Eb\nbF', result_buffer ) - - AssertBuffersAreEqualAsBytes( [ 'aAa', - 'aEb', - 'bFCa', - 'aDe' ], result_buffer ) - - -@patch( 'vim.current.window.cursor', ( 1, 1 ) ) -def ReplaceChunk_MultipleLinesToMoreMultipleLines_test(): - result_buffer = VimBuffer( 'buffer', contents = [ 'aAa', - 'aBa', - 'aCa', - 'aDe' ] ) - start, end = _BuildLocations( 2, 2, 3, 2 ) - vimsupport.ReplaceChunk( start, end, 'Eb\nbFb\nbG', result_buffer ) - - AssertBuffersAreEqualAsBytes( [ 'aAa', - 'aEb', - 'bFb', - 'bGCa', - 'aDe' ], result_buffer ) - - -@patch( 'vim.current.window.cursor', ( 1, 1 ) ) -def ReplaceChunk_MultipleLinesToLessMultipleLines_test(): - result_buffer = VimBuffer( 'buffer', contents = [ 'aAa', - 'aBa', - 'aCa', - 'aDe' ] ) - start, end = _BuildLocations( 1, 2, 3, 2 ) - vimsupport.ReplaceChunk( start, end, 'Eb\nbF', result_buffer ) - - AssertBuffersAreEqualAsBytes( [ 'aEb', - 'bFCa', - 'aDe' ], result_buffer ) - - -@patch( 'vim.current.window.cursor', ( 1, 1 ) ) -def ReplaceChunk_MultipleLinesToEvenLessMultipleLines_test(): - result_buffer = VimBuffer( 'buffer', contents = [ 'aAa', - 'aBa', - 'aCa', - 'aDe' ] ) - start, end = _BuildLocations( 1, 2, 4, 2 ) - vimsupport.ReplaceChunk( start, end, 'Eb\nbF', result_buffer ) - - AssertBuffersAreEqualAsBytes( [ 'aEb', - 'bFDe' ], result_buffer ) - - -@patch( 'vim.current.window.cursor', ( 1, 1 ) ) -def ReplaceChunk_SpanBufferEdge_test(): - result_buffer = VimBuffer( 'buffer', contents = [ 'aAa', - 'aBa', - 'aCa' ] ) - start, end = _BuildLocations( 1, 1, 1, 3 ) - vimsupport.ReplaceChunk( start, end, 'bDb', result_buffer ) - - AssertBuffersAreEqualAsBytes( [ 'bDba', - 'aBa', - 'aCa' ], result_buffer ) - - -@patch( 'vim.current.window.cursor', ( 1, 1 ) ) -def ReplaceChunk_DeleteTextInLine_test(): - result_buffer = VimBuffer( 'buffer', contents = [ 'aAa', - 'aBa', - 'aCa' ] ) - start, end = _BuildLocations( 2, 2, 2, 3 ) - vimsupport.ReplaceChunk( start, end, '', result_buffer ) - AssertBuffersAreEqualAsBytes( [ 'aAa', - 'aa', - 'aCa' ], result_buffer ) - - -@patch( 'vim.current.window.cursor', ( 1, 1 ) ) -def ReplaceChunk_AddTextInLine_test(): - result_buffer = VimBuffer( 'buffer', contents = [ 'aAa', - 'aBa', - 'aCa' ] ) - start, end = _BuildLocations( 2, 2, 2, 2 ) - vimsupport.ReplaceChunk( start, end, 'bDb', result_buffer ) - - AssertBuffersAreEqualAsBytes( [ 'aAa', - 'abDbBa', - 'aCa' ], result_buffer ) - - -@patch( 'vim.current.window.cursor', ( 1, 1 ) ) -def ReplaceChunk_ReplaceTextInLine_test(): - result_buffer = VimBuffer( 'buffer', contents = [ 'aAa', - 'aBa', - 'aCa' ] ) - start, end = _BuildLocations( 2, 2, 2, 3 ) - vimsupport.ReplaceChunk( start, end, 'bDb', result_buffer ) - - AssertBuffersAreEqualAsBytes( [ 'aAa', - 'abDba', - 'aCa' ], result_buffer ) - - -@patch( 'vim.current.window.cursor', ( 1, 1 ) ) -def ReplaceChunk_NewlineChunk_test(): - result_buffer = VimBuffer( 'buffer', contents = [ 'first line', - 'second line' ] ) - start, end = _BuildLocations( 1, 11, 2, 1 ) - vimsupport.ReplaceChunk( start, end, '\n', result_buffer ) - - AssertBuffersAreEqualAsBytes( [ 'first line', - 'second line' ], result_buffer ) - - -@patch( 'vim.current.window.cursor', ( 1, 1 ) ) -def ReplaceChunk_BeyondEndOfFile_test(): - result_buffer = VimBuffer( 'buffer', contents = [ 'first line', - 'second line' ] ) - start, end = _BuildLocations( 1, 11, 3, 1 ) - vimsupport.ReplaceChunk( start, end, '\n', result_buffer ) - - AssertBuffersAreEqualAsBytes( [ 'first line' ], result_buffer ) - + @patch( 'vim.current.window.cursor', ( 1, 1 ) ) + def test_ReplaceChunk_SingleLine_Del_1( self ): + # Delete from start + result_buffer = VimBuffer( 'buffer', contents = [ 'This is a string' ] ) + start, end = _BuildLocations( 1, 1, 1, 6 ) + vimsupport.ReplaceChunk( start, end, '', result_buffer ) -@patch( 'vim.current.window.cursor', ( 1, 3 ) ) -def ReplaceChunk_CursorPosition_test(): - result_buffer = VimBuffer( 'buffer', contents = [ 'bar' ] ) - start, end = _BuildLocations( 1, 1, 1, 1 ) - vimsupport.ReplaceChunk( start, - end, - 'xyz\nfoo', - result_buffer ) + AssertBuffersAreEqualAsBytes( [ 'is a string' ], result_buffer ) - AssertBuffersAreEqualAsBytes( [ 'xyz', 'foobar' ], result_buffer ) - # Cursor line is 0-based. - assert_that( vimsupport.CurrentLineAndColumn(), contains_exactly( 1, 6 ) ) + @patch( 'vim.current.window.cursor', ( 1, 1 ) ) + def test_ReplaceChunk_SingleLine_Del_2( self ): + # Delete from end + result_buffer = VimBuffer( 'buffer', contents = [ 'This is a string' ] ) + start, end = _BuildLocations( 1, 10, 1, 18 ) + vimsupport.ReplaceChunk( start, end, '', result_buffer ) -def _BuildLocations( start_line, start_column, end_line, end_column ): - return { - 'line_num' : start_line, - 'column_num': start_column, - }, { - 'line_num' : end_line, - 'column_num': end_column, - } + AssertBuffersAreEqualAsBytes( [ 'This is a' ], result_buffer ) -@patch( 'vim.current.window.cursor', ( 1, 1 ) ) -def ReplaceChunksInBuffer_SortedChunks_test(): - chunks = [ - _BuildChunk( 1, 4, 1, 4, '(' ), - _BuildChunk( 1, 11, 1, 11, ')' ) - ] - - result_buffer = VimBuffer( 'buffer', contents = [ 'CT<10 >> 2> ct' ] ) - vimsupport.ReplaceChunksInBuffer( chunks, result_buffer ) - - AssertBuffersAreEqualAsBytes( [ 'CT<(10 >> 2)> ct' ], result_buffer ) - - -@patch( 'vim.current.window.cursor', ( 1, 1 ) ) -def ReplaceChunksInBuffer_UnsortedChunks_test(): - chunks = [ - _BuildChunk( 1, 11, 1, 11, ')' ), - _BuildChunk( 1, 4, 1, 4, '(' ) - ] - - result_buffer = VimBuffer( 'buffer', contents = [ 'CT<10 >> 2> ct' ] ) - vimsupport.ReplaceChunksInBuffer( chunks, result_buffer ) - - AssertBuffersAreEqualAsBytes( [ 'CT<(10 >> 2)> ct' ], result_buffer ) - - -@patch( 'vim.current.window.cursor', ( 1, 1 ) ) -def ReplaceChunksInBuffer_LineOverlappingChunks_test(): - chunks = [ - _BuildChunk( 1, 11, 2, 1, '\n ' ), - _BuildChunk( 2, 12, 3, 1, '\n ' ), - _BuildChunk( 3, 11, 4, 1, '\n ' ) - ] - - result_buffer = VimBuffer( 'buffer', contents = [ 'first line', - 'second line', - 'third line', - 'fourth line' ] ) - vimsupport.ReplaceChunksInBuffer( chunks, result_buffer ) - - AssertBuffersAreEqualAsBytes( [ 'first line', - ' second line', - ' third line', - ' fourth line' ], result_buffer ) - - -@patch( 'vim.current.window.cursor', ( 1, 1 ) ) -def ReplaceChunksInBuffer_OutdentChunks_test(): - chunks = [ - _BuildChunk( 1, 1, 1, 5, ' ' ), - _BuildChunk( 1, 15, 2, 9, '\n ' ), - _BuildChunk( 2, 20, 3, 3, '\n' ) - ] - - result_buffer = VimBuffer( 'buffer', contents = [ ' first line', - ' second line', - ' third line' ] ) - vimsupport.ReplaceChunksInBuffer( chunks, result_buffer ) - - AssertBuffersAreEqualAsBytes( [ ' first line', - ' second line', - ' third line' ], result_buffer ) - - -@patch( 'vim.current.window.cursor', ( 1, 1 ) ) -def ReplaceChunksInBuffer_OneLineIndentingChunks_test(): - chunks = [ - _BuildChunk( 1, 8, 2, 1, '\n ' ), - _BuildChunk( 2, 9, 2, 10, '\n ' ), - _BuildChunk( 2, 19, 2, 20, '\n ' ) - ] - - result_buffer = VimBuffer( 'buffer', contents = [ 'class {', - 'method { statement }', - '}' ] ) - vimsupport.ReplaceChunksInBuffer( chunks, result_buffer ) - - AssertBuffersAreEqualAsBytes( [ 'class {', - ' method {', - ' statement', - ' }', - '}' ], result_buffer ) - - -@patch( 'vim.current.window.cursor', ( 1, 1 ) ) -def ReplaceChunksInBuffer_SameLocation_test(): - chunks = [ - _BuildChunk( 1, 1, 1, 1, 'this ' ), - _BuildChunk( 1, 1, 1, 1, 'is ' ), - _BuildChunk( 1, 1, 1, 1, 'pure ' ) - ] - - result_buffer = VimBuffer( 'buffer', contents = [ 'folly' ] ) - vimsupport.ReplaceChunksInBuffer( chunks, result_buffer ) - - AssertBuffersAreEqualAsBytes( [ 'this is pure folly' ], result_buffer ) - - -@patch( 'vim.current.window.cursor', ( 1, 1 ) ) -@patch( 'ycm.vimsupport.VariableExists', return_value = False ) -@patch( 'ycm.vimsupport.SetFittingHeightForCurrentWindow' ) -@patch( 'ycm.vimsupport.GetBufferNumberForFilename', - return_value = 1, - new_callable = ExtendedMock ) -@patch( 'ycm.vimsupport.BufferIsVisible', - return_value = True, - new_callable = ExtendedMock ) -@patch( 'ycm.vimsupport.OpenFilename' ) -@patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) -@patch( 'vim.eval', new_callable = ExtendedMock ) -@patch( 'vim.command', new_callable = ExtendedMock ) -def ReplaceChunks_SingleFile_Open_test( vim_command, - vim_eval, - post_vim_message, - open_filename, - buffer_is_visible, - get_buffer_number_for_filename, - set_fitting_height, - variable_exists ): - single_buffer_name = os.path.realpath( 'single_file' ) - - chunks = [ - _BuildChunk( 1, 1, 2, 1, 'replacement', single_buffer_name ) - ] - - result_buffer = VimBuffer( - single_buffer_name, - contents = [ - 'line1', - 'line2', - 'line3' + @patch( 'vim.current.window.cursor', ( 1, 1 ) ) + def test_ReplaceChunk_SingleLine_Del_3( self ): + # Delete from middle + result_buffer = VimBuffer( 'buffer', contents = [ 'This is not a string' ] ) + start, end = _BuildLocations( 1, 9, 1, 13 ) + vimsupport.ReplaceChunk( start, end, '', result_buffer ) + + AssertBuffersAreEqualAsBytes( [ 'This is a string' ], result_buffer ) + + + @patch( 'vim.current.window.cursor', ( 1, 1 ) ) + def test_ReplaceChunk_SingleLine_Unicode_ReplaceUnicodeChars( self ): + # Replace Unicode characters. + result_buffer = VimBuffer( + 'buffer', contents = [ 'This Uniçø∂‰ string is in the middle' ] ) + start, end = _BuildLocations( 1, 6, 1, 20 ) + vimsupport.ReplaceChunk( start, end, 'Unicode ', result_buffer ) + + AssertBuffersAreEqualAsBytes( [ 'This Unicode string is in the middle' ], + result_buffer ) + + + @patch( 'vim.current.window.cursor', ( 1, 1 ) ) + def test_ReplaceChunk_SingleLine_Unicode_ReplaceAfterUnicode( self ): + # Replace ASCII characters after Unicode characters in the line. + result_buffer = VimBuffer( + 'buffer', contents = [ 'This Uniçø∂‰ string is in the middle' ] ) + start, end = _BuildLocations( 1, 30, 1, 43 ) + vimsupport.ReplaceChunk( start, end, 'fåke', result_buffer ) + + AssertBuffersAreEqualAsBytes( [ 'This Uniçø∂‰ string is fåke' ], + result_buffer ) + + + @patch( 'vim.current.window.cursor', ( 1, 1 ) ) + def test_ReplaceChunk_SingleLine_Unicode_Grown( self ): + # Replace ASCII characters after Unicode characters in the line. + result_buffer = VimBuffer( 'buffer', contents = [ 'a' ] ) + start, end = _BuildLocations( 1, 1, 1, 2 ) + vimsupport.ReplaceChunk( start, end, 'å', result_buffer ) + + AssertBuffersAreEqualAsBytes( [ 'å' ], result_buffer ) + + + @patch( 'vim.current.window.cursor', ( 1, 1 ) ) + def test_ReplaceChunk_RemoveSingleLine( self ): + result_buffer = VimBuffer( 'buffer', contents = [ 'aAa', + 'aBa', + 'aCa' ] ) + start, end = _BuildLocations( 2, 1, 3, 1 ) + vimsupport.ReplaceChunk( start, end, '', result_buffer ) + # First line is not affected. + AssertBuffersAreEqualAsBytes( [ 'aAa', + 'aCa' ], result_buffer ) + + + @patch( 'vim.current.window.cursor', ( 1, 1 ) ) + def test_ReplaceChunk_SingleToMultipleLines( self ): + result_buffer = VimBuffer( 'buffer', contents = [ 'aAa', + 'aBa', + 'aCa' ] ) + start, end = _BuildLocations( 2, 3, 2, 4 ) + vimsupport.ReplaceChunk( start, end, 'cccc', result_buffer ) + + AssertBuffersAreEqualAsBytes( [ 'aAa', + 'aBcccc', + 'aCa' ], result_buffer ) + + # now make another change to the second line + start, end = _BuildLocations( 2, 2, 2, 2 ) + vimsupport.ReplaceChunk( start, end, 'Eb\nbF', result_buffer ) + + AssertBuffersAreEqualAsBytes( [ 'aAa', + 'aEb', + 'bFBcccc', + 'aCa' ], result_buffer ) + + + @patch( 'vim.current.window.cursor', ( 1, 1 ) ) + def test_ReplaceChunk_SingleToMultipleLines2( self ): + result_buffer = VimBuffer( 'buffer', contents = [ 'aAa', + 'aBa', + 'aCa' ] ) + start, end = _BuildLocations( 2, 2, 2, 2 ) + vimsupport.ReplaceChunk( start, end, 'Eb\nbFb\nG', result_buffer ) + + AssertBuffersAreEqualAsBytes( [ 'aAa', + 'aEb', + 'bFb', + 'GBa', + 'aCa' ], result_buffer ) + + + @patch( 'vim.current.window.cursor', ( 1, 1 ) ) + def test_ReplaceChunk_SingleToMultipleLines3( self ): + result_buffer = VimBuffer( 'buffer', contents = [ 'aAa', + 'aBa', + 'aCa' ] ) + start, end = _BuildLocations( 2, 2, 2, 2 ) + vimsupport.ReplaceChunk( start, end, 'Eb\nbFb\nbGb', result_buffer ) + + AssertBuffersAreEqualAsBytes( [ 'aAa', + 'aEb', + 'bFb', + 'bGbBa', + 'aCa' ], result_buffer ) + + + @patch( 'vim.current.window.cursor', ( 1, 1 ) ) + def test_ReplaceChunk_SingleToMultipleLinesReplace( self ): + result_buffer = VimBuffer( 'buffer', contents = [ 'aAa', + 'aBa', + 'aCa' ] ) + start, end = _BuildLocations( 1, 2, 1, 4 ) + vimsupport.ReplaceChunk( start, end, 'Eb\nbFb\nbGb', result_buffer ) + + AssertBuffersAreEqualAsBytes( [ 'aEb', + 'bFb', + 'bGb', + 'aBa', + 'aCa' ], result_buffer ) + + + @patch( 'vim.current.window.cursor', ( 1, 1 ) ) + def test_ReplaceChunk_SingleToMultipleLinesReplace_2( self ): + result_buffer = VimBuffer( 'buffer', contents = [ 'aAa', + 'aBa', + 'aCa' ] ) + start, end = _BuildLocations( 1, 4, 1, 4 ) + vimsupport.ReplaceChunk( start, end, 'cccc', result_buffer ) + + AssertBuffersAreEqualAsBytes( [ 'aAacccc', + 'aBa', + 'aCa', ], result_buffer ) + + # now do a subsequent change (insert in the middle of the first line) + start, end = _BuildLocations( 1, 2, 1, 4 ) + vimsupport.ReplaceChunk( start, end, 'Eb\nbFb\nbGb', result_buffer ) + + AssertBuffersAreEqualAsBytes( [ 'aEb', + 'bFb', + 'bGbcccc', + 'aBa', + 'aCa' ], result_buffer ) + + + @patch( 'vim.current.window.cursor', ( 1, 1 ) ) + def test_ReplaceChunk_MultipleLinesToSingleLine( self ): + result_buffer = VimBuffer( 'buffer', contents = [ 'aAa', + 'aBa', + 'aCaaaa' ] ) + start, end = _BuildLocations( 3, 4, 3, 5 ) + vimsupport.ReplaceChunk( start, end, 'dd\ndd', result_buffer ) + + AssertBuffersAreEqualAsBytes( [ 'aAa', + 'aBa', + 'aCadd', + 'ddaa' ], result_buffer ) + + # make another modification applying offsets + start, end = _BuildLocations( 3, 3, 3, 4 ) + vimsupport.ReplaceChunk( start, end, 'cccc', result_buffer ) + + AssertBuffersAreEqualAsBytes( [ 'aAa', + 'aBa', + 'aCccccdd', + 'ddaa' ], result_buffer ) + + # and another, for luck + start, end = _BuildLocations( 2, 2, 3, 2 ) + vimsupport.ReplaceChunk( start, end, 'E', result_buffer ) + + AssertBuffersAreEqualAsBytes( [ 'aAa', + 'aECccccdd', + 'ddaa' ], result_buffer ) + + + @patch( 'vim.current.window.cursor', ( 1, 1 ) ) + def test_ReplaceChunk_MultipleLinesToSameMultipleLines( self ): + result_buffer = VimBuffer( 'buffer', contents = [ 'aAa', + 'aBa', + 'aCa', + 'aDe' ] ) + start, end = _BuildLocations( 2, 2, 3, 2 ) + vimsupport.ReplaceChunk( start, end, 'Eb\nbF', result_buffer ) + + AssertBuffersAreEqualAsBytes( [ 'aAa', + 'aEb', + 'bFCa', + 'aDe' ], result_buffer ) + + + @patch( 'vim.current.window.cursor', ( 1, 1 ) ) + def test_ReplaceChunk_MultipleLinesToMoreMultipleLines( self ): + result_buffer = VimBuffer( 'buffer', contents = [ 'aAa', + 'aBa', + 'aCa', + 'aDe' ] ) + start, end = _BuildLocations( 2, 2, 3, 2 ) + vimsupport.ReplaceChunk( start, end, 'Eb\nbFb\nbG', result_buffer ) + + AssertBuffersAreEqualAsBytes( [ 'aAa', + 'aEb', + 'bFb', + 'bGCa', + 'aDe' ], result_buffer ) + + + @patch( 'vim.current.window.cursor', ( 1, 1 ) ) + def test_ReplaceChunk_MultipleLinesToLessMultipleLines( self ): + result_buffer = VimBuffer( 'buffer', contents = [ 'aAa', + 'aBa', + 'aCa', + 'aDe' ] ) + start, end = _BuildLocations( 1, 2, 3, 2 ) + vimsupport.ReplaceChunk( start, end, 'Eb\nbF', result_buffer ) + + AssertBuffersAreEqualAsBytes( [ 'aEb', + 'bFCa', + 'aDe' ], result_buffer ) + + + @patch( 'vim.current.window.cursor', ( 1, 1 ) ) + def test_ReplaceChunk_MultipleLinesToEvenLessMultipleLines( self ): + result_buffer = VimBuffer( 'buffer', contents = [ 'aAa', + 'aBa', + 'aCa', + 'aDe' ] ) + start, end = _BuildLocations( 1, 2, 4, 2 ) + vimsupport.ReplaceChunk( start, end, 'Eb\nbF', result_buffer ) + + AssertBuffersAreEqualAsBytes( [ 'aEb', + 'bFDe' ], result_buffer ) + + + @patch( 'vim.current.window.cursor', ( 1, 1 ) ) + def test_ReplaceChunk_SpanBufferEdge( self ): + result_buffer = VimBuffer( 'buffer', contents = [ 'aAa', + 'aBa', + 'aCa' ] ) + start, end = _BuildLocations( 1, 1, 1, 3 ) + vimsupport.ReplaceChunk( start, end, 'bDb', result_buffer ) + + AssertBuffersAreEqualAsBytes( [ 'bDba', + 'aBa', + 'aCa' ], result_buffer ) + + + @patch( 'vim.current.window.cursor', ( 1, 1 ) ) + def test_ReplaceChunk_DeleteTextInLine( self ): + result_buffer = VimBuffer( 'buffer', contents = [ 'aAa', + 'aBa', + 'aCa' ] ) + start, end = _BuildLocations( 2, 2, 2, 3 ) + vimsupport.ReplaceChunk( start, end, '', result_buffer ) + AssertBuffersAreEqualAsBytes( [ 'aAa', + 'aa', + 'aCa' ], result_buffer ) + + + @patch( 'vim.current.window.cursor', ( 1, 1 ) ) + def test_ReplaceChunk_AddTextInLine( self ): + result_buffer = VimBuffer( 'buffer', contents = [ 'aAa', + 'aBa', + 'aCa' ] ) + start, end = _BuildLocations( 2, 2, 2, 2 ) + vimsupport.ReplaceChunk( start, end, 'bDb', result_buffer ) + + AssertBuffersAreEqualAsBytes( [ 'aAa', + 'abDbBa', + 'aCa' ], result_buffer ) + + + @patch( 'vim.current.window.cursor', ( 1, 1 ) ) + def test_ReplaceChunk_ReplaceTextInLine( self ): + result_buffer = VimBuffer( 'buffer', contents = [ 'aAa', + 'aBa', + 'aCa' ] ) + start, end = _BuildLocations( 2, 2, 2, 3 ) + vimsupport.ReplaceChunk( start, end, 'bDb', result_buffer ) + + AssertBuffersAreEqualAsBytes( [ 'aAa', + 'abDba', + 'aCa' ], result_buffer ) + + + @patch( 'vim.current.window.cursor', ( 1, 1 ) ) + def test_ReplaceChunk_NewlineChunk( self ): + result_buffer = VimBuffer( 'buffer', contents = [ 'first line', + 'second line' ] ) + start, end = _BuildLocations( 1, 11, 2, 1 ) + vimsupport.ReplaceChunk( start, end, '\n', result_buffer ) + + AssertBuffersAreEqualAsBytes( [ 'first line', + 'second line' ], result_buffer ) + + + @patch( 'vim.current.window.cursor', ( 1, 1 ) ) + def test_ReplaceChunk_BeyondEndOfFile( self ): + result_buffer = VimBuffer( 'buffer', contents = [ 'first line', + 'second line' ] ) + start, end = _BuildLocations( 1, 11, 3, 1 ) + vimsupport.ReplaceChunk( start, end, '\n', result_buffer ) + + AssertBuffersAreEqualAsBytes( [ 'first line' ], result_buffer ) + + + @patch( 'vim.current.window.cursor', ( 1, 3 ) ) + def test_ReplaceChunk_CursorPosition( self ): + result_buffer = VimBuffer( 'buffer', contents = [ 'bar' ] ) + start, end = _BuildLocations( 1, 1, 1, 1 ) + vimsupport.ReplaceChunk( start, + end, + 'xyz\nfoo', + result_buffer ) + + AssertBuffersAreEqualAsBytes( [ 'xyz', 'foobar' ], result_buffer ) + # Cursor line is 0-based. + assert_that( vimsupport.CurrentLineAndColumn(), contains_exactly( 1, 6 ) ) + + + @patch( 'vim.current.window.cursor', ( 1, 1 ) ) + def test_ReplaceChunksInBuffer_SortedChunks( self ): + chunks = [ + _BuildChunk( 1, 4, 1, 4, '(' ), + _BuildChunk( 1, 11, 1, 11, ')' ) ] - ) - - with patch( 'vim.buffers', [ None, result_buffer, None ] ): - vimsupport.ReplaceChunks( chunks ) - - # Ensure that we applied the replacement correctly - assert_that( result_buffer.GetLines(), contains_exactly( - 'replacementline2', - 'line3', - ) ) - - # GetBufferNumberForFilename is called twice: - # - once to the check if we would require opening the file (so that we can - # raise a warning) - # - once whilst applying the changes - get_buffer_number_for_filename.assert_has_exact_calls( [ - call( single_buffer_name ), - call( single_buffer_name ), - ] ) - - # BufferIsVisible is called twice for the same reasons as above - buffer_is_visible.assert_has_exact_calls( [ - call( 1 ), - call( 1 ), - ] ) - - # we don't attempt to open any files - open_filename.assert_not_called() - - qflist = json.dumps( [ { - 'bufnr': 1, - 'filename': single_buffer_name, - 'lnum': 1, - 'col': 1, - 'text': 'replacement', - 'type': 'F' - } ] ) - # But we do set the quickfix list - vim_eval.assert_has_exact_calls( [ - call( f'setqflist( { qflist } )' ) - ] ) - - # And it is ReplaceChunks that prints the message showing the number of - # changes - post_vim_message.assert_has_exact_calls( [ - call( 'Applied 1 changes', warning = False ), - ] ) - - -@patch( 'vim.current.window.cursor', ( 1, 1 ) ) -@patch( 'ycm.vimsupport.VariableExists', return_value = False ) -@patch( 'ycm.vimsupport.SetFittingHeightForCurrentWindow' ) -@patch( 'ycm.vimsupport.GetBufferNumberForFilename', - side_effect = [ -1, -1, 1 ], - new_callable = ExtendedMock ) -@patch( 'ycm.vimsupport.BufferIsVisible', - side_effect = [ False, False, True ], - new_callable = ExtendedMock ) -@patch( 'ycm.vimsupport.OpenFilename', - new_callable = ExtendedMock ) -@patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) -@patch( 'ycm.vimsupport.Confirm', - return_value = True, - new_callable = ExtendedMock ) -@patch( 'vim.eval', return_value = 10, new_callable = ExtendedMock ) -@patch( 'vim.command', new_callable = ExtendedMock ) -def ReplaceChunks_SingleFile_NotOpen_test( vim_command, - vim_eval, - confirm, - post_vim_message, - open_filename, - buffer_is_visible, - get_buffer_number_for_filename, - set_fitting_height, - variable_exists ): - single_buffer_name = os.path.realpath( 'single_file' ) - - chunks = [ - _BuildChunk( 1, 1, 2, 1, 'replacement', single_buffer_name ) - ] - - result_buffer = VimBuffer( - single_buffer_name, - contents = [ - 'line1', - 'line2', - 'line3' + + result_buffer = VimBuffer( 'buffer', contents = [ 'CT<10 >> 2> ct' ] ) + vimsupport.ReplaceChunksInBuffer( chunks, result_buffer ) + + AssertBuffersAreEqualAsBytes( [ 'CT<(10 >> 2)> ct' ], result_buffer ) + + + @patch( 'vim.current.window.cursor', ( 1, 1 ) ) + def test_ReplaceChunksInBuffer_UnsortedChunks( self ): + chunks = [ + _BuildChunk( 1, 11, 1, 11, ')' ), + _BuildChunk( 1, 4, 1, 4, '(' ) ] - ) - - with patch( 'vim.buffers', [ None, result_buffer, None ] ): - vimsupport.ReplaceChunks( chunks ) - - # We checked if it was OK to open the file - confirm.assert_has_exact_calls( [ - call( vimsupport.FIXIT_OPENING_BUFFERS_MESSAGE_FORMAT.format( 1 ) ) - ] ) - - # Ensure that we applied the replacement correctly - assert_that( result_buffer.GetLines(), contains_exactly( - 'replacementline2', - 'line3', - ) ) - - # GetBufferNumberForFilename is called 3 times. The return values are set in - # the @patch call above: - # - once to the check if we would require opening the file (so that we can - # raise a warning) (-1 return) - # - once whilst applying the changes (-1 return) - # - finally after calling OpenFilename (1 return) - get_buffer_number_for_filename.assert_has_exact_calls( [ - call( single_buffer_name ), - call( single_buffer_name ), - call( single_buffer_name ), - ] ) - - # BufferIsVisible is called 3 times for the same reasons as above, with the - # return of each one - buffer_is_visible.assert_has_exact_calls( [ - call( -1 ), - call( -1 ), - call( 1 ), - ] ) - - # We open 'single_file' as expected. - open_filename.assert_called_with( single_buffer_name, { - 'focus': True, - 'fix': True, - 'size': 10 - } ) - - # And close it again, then show the quickfix window. - vim_command.assert_has_exact_calls( [ - call( 'lclose' ), - call( 'hide' ), - ] ) - - qflist = json.dumps( [ { - 'bufnr': 1, - 'filename': single_buffer_name, - 'lnum': 1, - 'col': 1, - 'text': 'replacement', - 'type': 'F' - } ] ) - # And update the quickfix list - vim_eval.assert_has_exact_calls( [ - call( '&previewheight' ), - call( f'setqflist( { qflist } )' ) - ] ) - - # And it is ReplaceChunks that prints the message showing the number of - # changes - post_vim_message.assert_has_exact_calls( [ - call( 'Applied 1 changes', warning = False ), - ] ) - - -@patch( 'vim.current.window.cursor', ( 1, 1 ) ) -@patch( 'ycm.vimsupport.VariableExists', return_value = False ) -@patch( 'ycm.vimsupport.SetFittingHeightForCurrentWindow' ) -@patch( 'ycm.vimsupport.GetBufferNumberForFilename', - side_effect = [ -1, 1 ], - new_callable = ExtendedMock ) -@patch( 'ycm.vimsupport.BufferIsVisible', - side_effect = [ False, True ], - new_callable = ExtendedMock ) -@patch( 'ycm.vimsupport.OpenFilename', - new_callable = ExtendedMock ) -@patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) -@patch( 'ycm.vimsupport.Confirm', - return_value = True, - new_callable = ExtendedMock ) -@patch( 'vim.eval', return_value = 10, new_callable = ExtendedMock ) -@patch( 'vim.command', new_callable = ExtendedMock ) -def ReplaceChunks_SingleFile_NotOpen_Silent_test( - vim_command, - vim_eval, - confirm, - post_vim_message, - open_filename, - buffer_is_visible, - get_buffer_number_for_filename, - set_fitting_height, - variable_exists ): - - # This test is the same as ReplaceChunks_SingleFile_NotOpen_test, but we pass - # the silent flag, as used by post-complete actions, and shows the stuff we - # _don't_ call in that case. - - single_buffer_name = os.path.realpath( 'single_file' ) - - chunks = [ - _BuildChunk( 1, 1, 2, 1, 'replacement', single_buffer_name ) - ] - - result_buffer = VimBuffer( - single_buffer_name, - contents = [ - 'line1', - 'line2', - 'line3' + + result_buffer = VimBuffer( 'buffer', contents = [ 'CT<10 >> 2> ct' ] ) + vimsupport.ReplaceChunksInBuffer( chunks, result_buffer ) + + AssertBuffersAreEqualAsBytes( [ 'CT<(10 >> 2)> ct' ], result_buffer ) + + + @patch( 'vim.current.window.cursor', ( 1, 1 ) ) + def test_ReplaceChunksInBuffer_LineOverlappingChunks( self ): + chunks = [ + _BuildChunk( 1, 11, 2, 1, '\n ' ), + _BuildChunk( 2, 12, 3, 1, '\n ' ), + _BuildChunk( 3, 11, 4, 1, '\n ' ) ] - ) - - with patch( 'vim.buffers', [ None, result_buffer, None ] ): - vimsupport.ReplaceChunks( chunks, silent=True ) - - # We didn't check if it was OK to open the file (silent) - confirm.assert_not_called() - - # Ensure that we applied the replacement correctly - assert_that( result_buffer.GetLines(), contains_exactly( - 'replacementline2', - 'line3', - ) ) - - # GetBufferNumberForFilename is called 2 times. The return values are set in - # the @patch call above: - # - once whilst applying the changes (-1 return) - # - finally after calling OpenFilename (1 return) - get_buffer_number_for_filename.assert_has_exact_calls( [ - call( single_buffer_name ), - call( single_buffer_name ), - ] ) - - # BufferIsVisible is called 2 times for the same reasons as above, with the - # return of each one - buffer_is_visible.assert_has_exact_calls( [ - call( -1 ), - call( 1 ), - ] ) - - # We open 'single_file' as expected. - open_filename.assert_called_with( single_buffer_name, { - 'focus': True, - 'fix': True, - 'size': 10 - } ) - - # And close it again, but don't show the quickfix window - vim_command.assert_has_exact_calls( [ - call( 'lclose' ), - call( 'hide' ), - ] ) - set_fitting_height.assert_not_called() - - # But we _don't_ update the QuickFix list - vim_eval.assert_has_exact_calls( [ - call( '&previewheight' ), - ] ) - - # And we don't print a message either - post_vim_message.assert_not_called() - - -@patch( 'vim.current.window.cursor', ( 1, 1 ) ) -@patch( 'ycm.vimsupport.GetBufferNumberForFilename', - side_effect = [ -1, -1, 1 ], - new_callable = ExtendedMock ) -@patch( 'ycm.vimsupport.BufferIsVisible', - side_effect = [ False, False, True ], - new_callable = ExtendedMock ) -@patch( 'ycm.vimsupport.OpenFilename', - new_callable = ExtendedMock ) -@patch( 'ycm.vimsupport.PostVimMessage', - new_callable = ExtendedMock ) -@patch( 'ycm.vimsupport.Confirm', - return_value = False, - new_callable = ExtendedMock ) -@patch( 'vim.eval', - return_value = 10, - new_callable = ExtendedMock ) -@patch( 'vim.command', new_callable = ExtendedMock ) -def ReplaceChunks_User_Declines_To_Open_File_test( - vim_command, - vim_eval, - confirm, - post_vim_message, - open_filename, - buffer_is_visible, - get_buffer_number_for_filename ): - - # Same as above, except the user selects Cancel when asked if they should - # allow us to open lots of (ahem, 1) file. - single_buffer_name = os.path.realpath( 'single_file' ) - - chunks = [ - _BuildChunk( 1, 1, 2, 1, 'replacement', single_buffer_name ) - ] - - result_buffer = VimBuffer( - single_buffer_name, - contents = [ - 'line1', - 'line2', - 'line3' + + result_buffer = VimBuffer( 'buffer', contents = [ 'first line', + 'second line', + 'third line', + 'fourth line' ] ) + vimsupport.ReplaceChunksInBuffer( chunks, result_buffer ) + + AssertBuffersAreEqualAsBytes( [ 'first line', + ' second line', + ' third line', + ' fourth line' ], result_buffer ) + + + @patch( 'vim.current.window.cursor', ( 1, 1 ) ) + def test_ReplaceChunksInBuffer_OutdentChunks( self ): + chunks = [ + _BuildChunk( 1, 1, 1, 5, ' ' ), + _BuildChunk( 1, 15, 2, 9, '\n ' ), + _BuildChunk( 2, 20, 3, 3, '\n' ) ] - ) - - with patch( 'vim.buffers', [ None, result_buffer, None ] ): - vimsupport.ReplaceChunks( chunks ) - - # We checked if it was OK to open the file - confirm.assert_has_exact_calls( [ - call( vimsupport.FIXIT_OPENING_BUFFERS_MESSAGE_FORMAT.format( 1 ) ) - ] ) - - # Ensure that buffer is not changed - assert_that( result_buffer.GetLines(), contains_exactly( - 'line1', - 'line2', - 'line3', - ) ) - - # GetBufferNumberForFilename is called once. The return values are set in - # the @patch call above: - # - once to the check if we would require opening the file (so that we can - # raise a warning) (-1 return) - get_buffer_number_for_filename.assert_has_exact_calls( [ - call( single_buffer_name ), - ] ) - - # BufferIsVisible is called once for the above file, which wasn't visible. - buffer_is_visible.assert_has_exact_calls( [ - call( -1 ), - ] ) - - # We don't attempt to open any files or update any quickfix list or anything - # like that - open_filename.assert_not_called() - vim_eval.assert_not_called() - vim_command.assert_not_called() - post_vim_message.assert_not_called() - - -@patch( 'vim.current.window.cursor', ( 1, 1 ) ) -@patch( 'ycm.vimsupport.GetBufferNumberForFilename', - side_effect = [ -1, -1, 1 ], - new_callable = ExtendedMock ) -# Key difference is here: In the final check, BufferIsVisible returns False -@patch( 'ycm.vimsupport.BufferIsVisible', - side_effect = [ False, False, False ], - new_callable = ExtendedMock ) -@patch( 'ycm.vimsupport.OpenFilename', - new_callable = ExtendedMock ) -@patch( 'ycm.vimsupport.PostVimMessage', - new_callable = ExtendedMock ) -@patch( 'ycm.vimsupport.Confirm', - return_value = True, - new_callable = ExtendedMock ) -@patch( 'vim.eval', - return_value = 10, - new_callable = ExtendedMock ) -@patch( 'vim.command', - new_callable = ExtendedMock ) -def ReplaceChunks_User_Aborts_Opening_File_test( - vim_command, - vim_eval, - confirm, - post_vim_message, - open_filename, - buffer_is_visible, - get_buffer_number_for_filename ): - - # Same as above, except the user selects Abort or Quick during the - # "swap-file-found" dialog - single_buffer_name = os.path.realpath( 'single_file' ) - - chunks = [ - _BuildChunk( 1, 1, 2, 1, 'replacement', single_buffer_name ) - ] - - result_buffer = VimBuffer( - single_buffer_name, - contents = [ - 'line1', - 'line2', - 'line3' + + result_buffer = VimBuffer( 'buffer', contents = [ ' first line', + ' second line', + ' third line' ] ) + vimsupport.ReplaceChunksInBuffer( chunks, result_buffer ) + + AssertBuffersAreEqualAsBytes( [ ' first line', + ' second line', + ' third line' ], result_buffer ) + + + @patch( 'vim.current.window.cursor', ( 1, 1 ) ) + def test_ReplaceChunksInBuffer_OneLineIndentingChunks( self ): + chunks = [ + _BuildChunk( 1, 8, 2, 1, '\n ' ), + _BuildChunk( 2, 9, 2, 10, '\n ' ), + _BuildChunk( 2, 19, 2, 20, '\n ' ) ] - ) - with patch( 'vim.buffers', [ None, result_buffer, None ] ): - assert_that( calling( vimsupport.ReplaceChunks ).with_args( chunks ), - raises( RuntimeError, - 'Unable to open file: .+single_file\n' - 'FixIt/Refactor operation aborted prior to completion. ' - 'Your files have not been fully updated. ' - 'Please use undo commands to revert the applied changes.' ) ) - - # We checked if it was OK to open the file - confirm.assert_has_exact_calls( [ - call( vimsupport.FIXIT_OPENING_BUFFERS_MESSAGE_FORMAT.format( 1 ) ) - ] ) - - # Ensure that buffer is not changed - assert_that( result_buffer.GetLines(), contains_exactly( - 'line1', - 'line2', - 'line3', - ) ) - - # We tried to open this file - open_filename.assert_called_with( single_buffer_name, { - 'focus': True, - 'fix': True, - 'size': 10 - } ) - vim_eval.assert_called_with( "&previewheight" ) - - # But raised an exception before issuing the message at the end - post_vim_message.assert_not_called() - - -@patch( 'vim.current.window.cursor', ( 1, 1 ) ) -@patch( 'ycm.vimsupport.VariableExists', return_value = False ) -@patch( 'ycm.vimsupport.SetFittingHeightForCurrentWindow' ) -@patch( 'ycm.vimsupport.GetBufferNumberForFilename', side_effect = [ - 22, # first_file (check) - -1, # second_file (check) - 22, # first_file (apply) - -1, # second_file (apply) - 19, # second_file (check after open) - ], - new_callable = ExtendedMock ) -@patch( 'ycm.vimsupport.BufferIsVisible', side_effect = [ - True, # first_file (check) - False, # second_file (check) - True, # first_file (apply) - False, # second_file (apply) - True, # side_effect (check after open) - ], - new_callable = ExtendedMock ) -@patch( 'ycm.vimsupport.OpenFilename', - new_callable = ExtendedMock ) -@patch( 'ycm.vimsupport.PostVimMessage', - new_callable = ExtendedMock ) -@patch( 'ycm.vimsupport.Confirm', return_value = True, - new_callable = ExtendedMock ) -@patch( 'vim.eval', return_value = 10, - new_callable = ExtendedMock ) -@patch( 'vim.command', - new_callable = ExtendedMock ) -def ReplaceChunks_MultiFile_Open_test( vim_command, - vim_eval, - confirm, - post_vim_message, - open_filename, - buffer_is_visible, - get_buffer_number_for_filename, - set_fitting_height, - variable_exists ): - - # Chunks are split across 2 files, one is already open, one isn't - first_buffer_name = os.path.realpath( '1_first_file' ) - second_buffer_name = os.path.realpath( '2_second_file' ) - - chunks = [ - _BuildChunk( 1, 1, 2, 1, 'first_file_replacement ', first_buffer_name ), - _BuildChunk( 2, 1, 2, 1, 'second_file_replacement ', second_buffer_name ), - ] - - first_file = VimBuffer( - first_buffer_name, - number = 22, - contents = [ - 'line1', - 'line2', - 'line3', + result_buffer = VimBuffer( 'buffer', contents = [ 'class {', + 'method { statement }', + '}' ] ) + vimsupport.ReplaceChunksInBuffer( chunks, result_buffer ) + + AssertBuffersAreEqualAsBytes( [ 'class {', + ' method {', + ' statement', + ' }', + '}' ], result_buffer ) + + + @patch( 'vim.current.window.cursor', ( 1, 1 ) ) + def test_ReplaceChunksInBuffer_SameLocation( self ): + chunks = [ + _BuildChunk( 1, 1, 1, 1, 'this ' ), + _BuildChunk( 1, 1, 1, 1, 'is ' ), + _BuildChunk( 1, 1, 1, 1, 'pure ' ) ] - ) - second_file = VimBuffer( - second_buffer_name, - number = 19, - contents = [ - 'another line1', - 'ACME line2', + + result_buffer = VimBuffer( 'buffer', contents = [ 'folly' ] ) + vimsupport.ReplaceChunksInBuffer( chunks, result_buffer ) + + AssertBuffersAreEqualAsBytes( [ 'this is pure folly' ], result_buffer ) + + + @patch( 'vim.current.window.cursor', ( 1, 1 ) ) + @patch( 'ycm.vimsupport.VariableExists', return_value = False ) + @patch( 'ycm.vimsupport.SetFittingHeightForCurrentWindow' ) + @patch( 'vim.command', new_callable = ExtendedMock ) + @patch( 'ycm.vimsupport.GetBufferNumberForFilename', + return_value = 1, + new_callable = ExtendedMock ) + @patch( 'ycm.vimsupport.BufferIsVisible', + return_value = True, + new_callable = ExtendedMock ) + @patch( 'ycm.vimsupport.OpenFilename' ) + @patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) + @patch( 'vim.eval', new_callable = ExtendedMock ) + def test_ReplaceChunks_SingleFile_Open( self, + vim_eval, + post_vim_message, + open_filename, + buffer_is_visible, + get_buffer_number_for_filename, + *args ): + single_buffer_name = os.path.realpath( 'single_file' ) + + chunks = [ + _BuildChunk( 1, 1, 2, 1, 'replacement', single_buffer_name ) ] - ) - - vim_buffers = [ None ] * 23 - vim_buffers[ 22 ] = first_file - vim_buffers[ 19 ] = second_file - - with patch( 'vim.buffers', vim_buffers ): - vimsupport.ReplaceChunks( chunks ) - - # We checked for the right file names - get_buffer_number_for_filename.assert_has_exact_calls( [ - call( first_buffer_name ), - call( second_buffer_name ), - call( first_buffer_name ), - call( second_buffer_name ), - call( second_buffer_name ), - ] ) - - # We checked if it was OK to open the file - confirm.assert_has_exact_calls( [ - call( vimsupport.FIXIT_OPENING_BUFFERS_MESSAGE_FORMAT.format( 1 ) ) - ] ) - - # Ensure that buffers are updated - assert_that( second_file.GetLines(), contains_exactly( - 'another line1', - 'second_file_replacement ACME line2', - ) ) - assert_that( first_file.GetLines(), contains_exactly( - 'first_file_replacement line2', - 'line3', - ) ) - - # We open '2_second_file' as expected. - open_filename.assert_called_with( second_buffer_name, { - 'focus': True, - 'fix': True, - 'size': 10 - } ) - - # And close it again, then show the quickfix window. - vim_command.assert_has_exact_calls( [ - call( 'lclose' ), - call( 'hide' ), - ] ) - - qflist = json.dumps( [ { - 'bufnr': 22, - 'filename': first_buffer_name, - 'lnum': 1, - 'col': 1, - 'text': 'first_file_replacement ', - 'type': 'F' - }, { - 'bufnr': 19, - 'filename': second_buffer_name, - 'lnum': 2, - 'col': 1, - 'text': 'second_file_replacement ', - 'type': 'F' - } ] ) - # And update the quickfix list with each entry - vim_eval.assert_has_exact_calls( [ - call( '&previewheight' ), - call( f'setqflist( { qflist } )' ) - ] ) - - # And it is ReplaceChunks that prints the message showing the number of - # changes - post_vim_message.assert_has_exact_calls( [ - call( 'Applied 2 changes', warning = False ), - ] ) + result_buffer = VimBuffer( + single_buffer_name, + contents = [ + 'line1', + 'line2', + 'line3' + ] + ) -def _BuildChunk( start_line, - start_column, - end_line, - end_column, - replacement_text, filepath='test_file_name' ): - return { - 'range': { - 'start': { - 'filepath': filepath, - 'line_num': start_line, - 'column_num': start_column, - }, - 'end': { - 'filepath': filepath, - 'line_num': end_line, - 'column_num': end_column, - }, - }, - 'replacement_text': replacement_text - } + with patch( 'vim.buffers', [ None, result_buffer, None ] ): + vimsupport.ReplaceChunks( chunks ) + + # Ensure that we applied the replacement correctly + assert_that( result_buffer.GetLines(), contains_exactly( + 'replacementline2', + 'line3', + ) ) + + # GetBufferNumberForFilename is called twice: + # - once to the check if we would require opening the file (so that we can + # raise a warning) + # - once whilst applying the changes + get_buffer_number_for_filename.assert_has_exact_calls( [ + call( single_buffer_name ), + call( single_buffer_name ), + ] ) + # BufferIsVisible is called twice for the same reasons as above + buffer_is_visible.assert_has_exact_calls( [ + call( 1 ), + call( 1 ), + ] ) -def GetDiagnosticMatchPattern_ErrorInMiddleOfLine_test(): - current_buffer = VimBuffer( - 'some_file', - contents = [ 'Highlight this error please' ] - ) + # we don't attempt to open any files + open_filename.assert_not_called() + + qflist = json.dumps( [ { + 'bufnr': 1, + 'filename': single_buffer_name, + 'lnum': 1, + 'col': 1, + 'text': 'replacement', + 'type': 'F' + } ] ) + # But we do set the quickfix list + vim_eval.assert_has_exact_calls( [ + call( f'setqflist( { qflist } )' ) + ] ) - with patch( 'vim.current.buffer', current_buffer ): - assert_that( - vimsupport.GetDiagnosticMatchPattern( 1, 16, 1, 21 ), - equal_to( '\\%1l\\%16c\\_.\\{-}\\%1l\\%21c' ) - ) + # And it is ReplaceChunks that prints the message showing the number of + # changes + post_vim_message.assert_has_exact_calls( [ + call( 'Applied 1 changes', warning = False ), + ] ) -def AddDiagnosticSyntaxMatch_WarningAtEndOfLine_test(): - current_buffer = VimBuffer( - 'some_file', - contents = [ 'Highlight this warning' ] - ) + @patch( 'vim.current.window.cursor', ( 1, 1 ) ) + @patch( 'ycm.vimsupport.VariableExists', return_value = False ) + @patch( 'ycm.vimsupport.SetFittingHeightForCurrentWindow' ) + @patch( 'ycm.vimsupport.GetBufferNumberForFilename', + side_effect = [ -1, -1, 1 ], + new_callable = ExtendedMock ) + @patch( 'ycm.vimsupport.BufferIsVisible', + side_effect = [ False, False, True ], + new_callable = ExtendedMock ) + @patch( 'ycm.vimsupport.OpenFilename', + new_callable = ExtendedMock ) + @patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) + @patch( 'ycm.vimsupport.Confirm', + return_value = True, + new_callable = ExtendedMock ) + @patch( 'vim.eval', return_value = 10, new_callable = ExtendedMock ) + @patch( 'vim.command', new_callable = ExtendedMock ) + def test_ReplaceChunks_SingleFile_NotOpen( self, + vim_command, + vim_eval, + confirm, + post_vim_message, + open_filename, + buffer_is_visible, + get_buffer_number_for_filename, + set_fitting_height, + variable_exists ): + single_buffer_name = os.path.realpath( 'single_file' ) + + chunks = [ + _BuildChunk( 1, 1, 2, 1, 'replacement', single_buffer_name ) + ] - with patch( 'vim.current.buffer', current_buffer ): - assert_that( - vimsupport.GetDiagnosticMatchPattern( 1, 16, 1, 23 ), - equal_to( '\\%1l\\%16c\\_.\\{-}\\%1l\\%23c' ) + result_buffer = VimBuffer( + single_buffer_name, + contents = [ + 'line1', + 'line2', + 'line3' + ] ) + with patch( 'vim.buffers', [ None, result_buffer, None ] ): + vimsupport.ReplaceChunks( chunks ) -def AddDiagnosticSyntaxMatch_UnicodeAtEndOfLine_test(): - current_buffer = VimBuffer( - 'some_file', - contents = [ 'Highlight unicøde' ] - ) + # We checked if it was OK to open the file + confirm.assert_has_exact_calls( [ + call( vimsupport.FIXIT_OPENING_BUFFERS_MESSAGE_FORMAT.format( 1 ) ) + ] ) - with patch( 'vim.current.buffer', current_buffer ): - assert_that( - vimsupport.GetDiagnosticMatchPattern( 1, 16, 1, 19 ), - equal_to( '\\%1l\\%16c\\_.\\{-}\\%1l\\%19c' ) - ) + # Ensure that we applied the replacement correctly + assert_that( result_buffer.GetLines(), contains_exactly( + 'replacementline2', + 'line3', + ) ) + + # GetBufferNumberForFilename is called 3 times. The return values are set in + # the @patch call above: + # - once to the check if we would require opening the file (so that we can + # raise a warning) (-1 return) + # - once whilst applying the changes (-1 return) + # - finally after calling OpenFilename (1 return) + get_buffer_number_for_filename.assert_has_exact_calls( [ + call( single_buffer_name ), + call( single_buffer_name ), + call( single_buffer_name ), + ] ) + # BufferIsVisible is called 3 times for the same reasons as above, with the + # return of each one + buffer_is_visible.assert_has_exact_calls( [ + call( -1 ), + call( -1 ), + call( 1 ), + ] ) -def AddDiagnosticSyntaxMatch_NonPositivePosition_test(): - current_buffer = VimBuffer( - 'some_file', - contents = [ 'Some contents' ] - ) + # We open 'single_file' as expected. + open_filename.assert_called_with( single_buffer_name, { + 'focus': True, + 'fix': True, + 'size': 10 + } ) + + # And close it again, then show the quickfix window. + vim_command.assert_has_exact_calls( [ + call( 'lclose' ), + call( 'hide' ), + ] ) + + qflist = json.dumps( [ { + 'bufnr': 1, + 'filename': single_buffer_name, + 'lnum': 1, + 'col': 1, + 'text': 'replacement', + 'type': 'F' + } ] ) + # And update the quickfix list + vim_eval.assert_has_exact_calls( [ + call( '&previewheight' ), + call( f'setqflist( { qflist } )' ) + ] ) + + # And it is ReplaceChunks that prints the message showing the number of + # changes + post_vim_message.assert_has_exact_calls( [ + call( 'Applied 1 changes', warning = False ), + ] ) - with patch( 'vim.current.buffer', current_buffer ): - assert_that( - vimsupport.GetDiagnosticMatchPattern( 0, 0, 0, 0 ), - equal_to( '\\%1l\\%1c\\_.\\{-}\\%1l\\%1c' ) - ) - assert_that( - vimsupport.GetDiagnosticMatchPattern( -1, -2, -3, -4 ), - equal_to( '\\%1l\\%1c\\_.\\{-}\\%1l\\%1c' ) + @patch( 'vim.current.window.cursor', ( 1, 1 ) ) + @patch( 'ycm.vimsupport.VariableExists', return_value = False ) + @patch( 'ycm.vimsupport.SetFittingHeightForCurrentWindow' ) + @patch( 'ycm.vimsupport.GetBufferNumberForFilename', + side_effect = [ -1, 1 ], + new_callable = ExtendedMock ) + @patch( 'ycm.vimsupport.BufferIsVisible', + side_effect = [ False, True ], + new_callable = ExtendedMock ) + @patch( 'ycm.vimsupport.OpenFilename', + new_callable = ExtendedMock ) + @patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) + @patch( 'ycm.vimsupport.Confirm', + return_value = True, + new_callable = ExtendedMock ) + @patch( 'vim.eval', return_value = 10, new_callable = ExtendedMock ) + @patch( 'vim.command', new_callable = ExtendedMock ) + def test_ReplaceChunks_SingleFile_NotOpen_Silent( + self, + vim_command, + vim_eval, + confirm, + post_vim_message, + open_filename, + buffer_is_visible, + get_buffer_number_for_filename, + set_fitting_height, + variable_exists ): + + # This test is the same as ReplaceChunks_SingleFile_NotOpen_test, but we + # pass the silent flag, as used by post-complete actions, and shows the + # stuff we _don't_ call in that case. + + single_buffer_name = os.path.realpath( 'single_file' ) + + chunks = [ + _BuildChunk( 1, 1, 2, 1, 'replacement', single_buffer_name ) + ] + + result_buffer = VimBuffer( + single_buffer_name, + contents = [ + 'line1', + 'line2', + 'line3' + ] ) + with patch( 'vim.buffers', [ None, result_buffer, None ] ): + vimsupport.ReplaceChunks( chunks, silent=True ) -@patch( 'vim.command', new_callable=ExtendedMock ) -@patch( 'vim.current', new_callable=ExtendedMock ) -def WriteToPreviewWindow_test( vim_current, vim_command ): - vim_current.window.options.__getitem__ = MagicMock( return_value = True ) + # We didn't check if it was OK to open the file (silent) + confirm.assert_not_called() - vimsupport.WriteToPreviewWindow( "test" ) + # Ensure that we applied the replacement correctly + assert_that( result_buffer.GetLines(), contains_exactly( + 'replacementline2', + 'line3', + ) ) + + # GetBufferNumberForFilename is called 2 times. The return values are set in + # the @patch call above: + # - once whilst applying the changes (-1 return) + # - finally after calling OpenFilename (1 return) + get_buffer_number_for_filename.assert_has_exact_calls( [ + call( single_buffer_name ), + call( single_buffer_name ), + ] ) - vim_command.assert_has_exact_calls( [ - call( 'silent! pclose!' ), - call( 'silent! pedit! _TEMP_FILE_' ), - call( 'silent! wincmd P' ), - call( 'silent! wincmd p' ) ] ) + # BufferIsVisible is called 2 times for the same reasons as above, with the + # return of each one + buffer_is_visible.assert_has_exact_calls( [ + call( -1 ), + call( 1 ), + ] ) - vim_current.buffer.__setitem__.assert_called_with( - slice( None, None, None ), [ 'test' ] ) + # We open 'single_file' as expected. + open_filename.assert_called_with( single_buffer_name, { + 'focus': True, + 'fix': True, + 'size': 10 + } ) - vim_current.buffer.options.__setitem__.assert_has_exact_calls( [ - call( 'modifiable', True ), - call( 'readonly', False ), - call( 'buftype', 'nofile' ), - call( 'bufhidden', 'wipe' ), - call( 'buflisted', False ), - call( 'swapfile', False ), - call( 'modifiable', False ), - call( 'modified', False ), - call( 'readonly', True ), - ], any_order = True ) + # And close it again, but don't show the quickfix window + vim_command.assert_has_exact_calls( [ + call( 'lclose' ), + call( 'hide' ), + ] ) + set_fitting_height.assert_not_called() + # But we _don't_ update the QuickFix list + vim_eval.assert_has_exact_calls( [ + call( '&previewheight' ), + ] ) -@patch( 'vim.current' ) -def WriteToPreviewWindow_MultiLine_test( vim_current ): - vim_current.window.options.__getitem__ = MagicMock( return_value = True ) - vimsupport.WriteToPreviewWindow( "test\ntest2" ) + # And we don't print a message either + post_vim_message.assert_not_called() + + + @patch( 'vim.current.window.cursor', ( 1, 1 ) ) + @patch( 'ycm.vimsupport.GetBufferNumberForFilename', + side_effect = [ -1, -1, 1 ], + new_callable = ExtendedMock ) + @patch( 'ycm.vimsupport.BufferIsVisible', + side_effect = [ False, False, True ], + new_callable = ExtendedMock ) + @patch( 'ycm.vimsupport.OpenFilename', + new_callable = ExtendedMock ) + @patch( 'ycm.vimsupport.PostVimMessage', + new_callable = ExtendedMock ) + @patch( 'ycm.vimsupport.Confirm', + return_value = False, + new_callable = ExtendedMock ) + @patch( 'vim.eval', + return_value = 10, + new_callable = ExtendedMock ) + @patch( 'vim.command', new_callable = ExtendedMock ) + def test_ReplaceChunks_User_Declines_To_Open_File( + self, + vim_command, + vim_eval, + confirm, + post_vim_message, + open_filename, + buffer_is_visible, + get_buffer_number_for_filename ): + + # Same as above, except the user selects Cancel when asked if they should + # allow us to open lots of (ahem, 1) file. + single_buffer_name = os.path.realpath( 'single_file' ) + + chunks = [ + _BuildChunk( 1, 1, 2, 1, 'replacement', single_buffer_name ) + ] - vim_current.buffer.__setitem__.assert_called_with( - slice( None, None, None ), [ 'test', 'test2' ] ) + result_buffer = VimBuffer( + single_buffer_name, + contents = [ + 'line1', + 'line2', + 'line3' + ] + ) + with patch( 'vim.buffers', [ None, result_buffer, None ] ): + vimsupport.ReplaceChunks( chunks ) -@patch( 'vim.command', new_callable=ExtendedMock ) -@patch( 'vim.current', new_callable=ExtendedMock ) -def WriteToPreviewWindow_JumpFail_test( vim_current, vim_command ): - vim_current.window.options.__getitem__ = MagicMock( return_value = False ) + # We checked if it was OK to open the file + confirm.assert_has_exact_calls( [ + call( vimsupport.FIXIT_OPENING_BUFFERS_MESSAGE_FORMAT.format( 1 ) ) + ] ) - vimsupport.WriteToPreviewWindow( "test" ) + # Ensure that buffer is not changed + assert_that( result_buffer.GetLines(), contains_exactly( + 'line1', + 'line2', + 'line3', + ) ) + + # GetBufferNumberForFilename is called once. The return values are set in + # the @patch call above: + # - once to the check if we would require opening the file (so that we can + # raise a warning) (-1 return) + get_buffer_number_for_filename.assert_has_exact_calls( [ + call( single_buffer_name ), + ] ) - vim_command.assert_has_exact_calls( [ - call( 'silent! pclose!' ), - call( 'silent! pedit! _TEMP_FILE_' ), - call( 'silent! wincmd P' ), - call( 'redraw' ), - call( "echo 'test'" ), - ] ) + # BufferIsVisible is called once for the above file, which wasn't visible. + buffer_is_visible.assert_has_exact_calls( [ + call( -1 ), + ] ) - vim_current.buffer.__setitem__.assert_not_called() - vim_current.buffer.options.__setitem__.assert_not_called() + # We don't attempt to open any files or update any quickfix list or anything + # like that + open_filename.assert_not_called() + vim_eval.assert_not_called() + vim_command.assert_not_called() + post_vim_message.assert_not_called() + + + @patch( 'vim.current.window.cursor', ( 1, 1 ) ) + @patch( 'ycm.vimsupport.GetBufferNumberForFilename', + side_effect = [ -1, -1, 1 ], + new_callable = ExtendedMock ) + # Key difference is here: In the final check, BufferIsVisible returns False + @patch( 'ycm.vimsupport.BufferIsVisible', + side_effect = [ False, False, False ], + new_callable = ExtendedMock ) + @patch( 'ycm.vimsupport.OpenFilename', + new_callable = ExtendedMock ) + @patch( 'ycm.vimsupport.PostVimMessage', + new_callable = ExtendedMock ) + @patch( 'ycm.vimsupport.Confirm', + return_value = True, + new_callable = ExtendedMock ) + @patch( 'vim.eval', + return_value = 10, + new_callable = ExtendedMock ) + @patch( 'vim.command', + new_callable = ExtendedMock ) + def test_ReplaceChunks_User_Aborts_Opening_File( + self, + vim_command, + vim_eval, + confirm, + post_vim_message, + open_filename, + buffer_is_visible, + get_buffer_number_for_filename ): + + # Same as above, except the user selects Abort or Quick during the + # "swap-file-found" dialog + single_buffer_name = os.path.realpath( 'single_file' ) + + chunks = [ + _BuildChunk( 1, 1, 2, 1, 'replacement', single_buffer_name ) + ] + result_buffer = VimBuffer( + single_buffer_name, + contents = [ + 'line1', + 'line2', + 'line3' + ] + ) -@patch( 'vim.command', new_callable=ExtendedMock ) -@patch( 'vim.current', new_callable=ExtendedMock ) -def WriteToPreviewWindow_JumpFail_MultiLine_test( vim_current, vim_command ): + with patch( 'vim.buffers', [ None, result_buffer, None ] ): + assert_that( + calling( vimsupport.ReplaceChunks ).with_args( chunks ), + raises( RuntimeError, + 'Unable to open file: .+single_file\n' + 'FixIt/Refactor operation aborted prior to completion. ' + 'Your files have not been fully updated. ' + 'Please use undo commands to revert the applied changes.' ) ) + + # We checked if it was OK to open the file + confirm.assert_has_exact_calls( [ + call( vimsupport.FIXIT_OPENING_BUFFERS_MESSAGE_FORMAT.format( 1 ) ) + ] ) - vim_current.window.options.__getitem__ = MagicMock( return_value = False ) + # Ensure that buffer is not changed + assert_that( result_buffer.GetLines(), contains_exactly( + 'line1', + 'line2', + 'line3', + ) ) + + # We tried to open this file + open_filename.assert_called_with( single_buffer_name, { + 'focus': True, + 'fix': True, + 'size': 10 + } ) + vim_eval.assert_called_with( "&previewheight" ) + + # But raised an exception before issuing the message at the end + post_vim_message.assert_not_called() + + + @patch( 'vim.current.window.cursor', ( 1, 1 ) ) + @patch( 'ycm.vimsupport.VariableExists', return_value = False ) + @patch( 'ycm.vimsupport.SetFittingHeightForCurrentWindow' ) + @patch( 'ycm.vimsupport.GetBufferNumberForFilename', side_effect = [ + 22, # first_file (check) + -1, # second_file (check) + 22, # first_file (apply) + -1, # second_file (apply) + 19, # second_file (check after open) + ], + new_callable = ExtendedMock ) + @patch( 'ycm.vimsupport.BufferIsVisible', side_effect = [ + True, # first_file (check) + False, # second_file (check) + True, # first_file (apply) + False, # second_file (apply) + True, # side_effect (check after open) + ], + new_callable = ExtendedMock ) + @patch( 'ycm.vimsupport.OpenFilename', + new_callable = ExtendedMock ) + @patch( 'ycm.vimsupport.PostVimMessage', + new_callable = ExtendedMock ) + @patch( 'ycm.vimsupport.Confirm', return_value = True, + new_callable = ExtendedMock ) + @patch( 'vim.eval', return_value = 10, + new_callable = ExtendedMock ) + @patch( 'vim.command', + new_callable = ExtendedMock ) + def test_ReplaceChunks_MultiFile_Open( self, + vim_command, + vim_eval, + confirm, + post_vim_message, + open_filename, + buffer_is_visible, + get_buffer_number_for_filename, + set_fitting_height, + variable_exists ): + + # Chunks are split across 2 files, one is already open, one isn't + first_buffer_name = os.path.realpath( '1_first_file' ) + second_buffer_name = os.path.realpath( '2_second_file' ) + + chunks = [ + _BuildChunk( 1, 1, 2, 1, 'first_file_replacement ', first_buffer_name ), + _BuildChunk( 2, 1, 2, 1, 'second_file_replacement ', second_buffer_name ), + ] - vimsupport.WriteToPreviewWindow( "test\ntest2" ) + first_file = VimBuffer( + first_buffer_name, + number = 22, + contents = [ + 'line1', + 'line2', + 'line3', + ] + ) + second_file = VimBuffer( + second_buffer_name, + number = 19, + contents = [ + 'another line1', + 'ACME line2', + ] + ) - vim_command.assert_has_exact_calls( [ - call( 'silent! pclose!' ), - call( 'silent! pedit! _TEMP_FILE_' ), - call( 'silent! wincmd P' ), - call( 'redraw' ), - call( "echo 'test'" ), - call( "echo 'test2'" ), - ] ) + vim_buffers = [ None ] * 23 + vim_buffers[ 22 ] = first_file + vim_buffers[ 19 ] = second_file - vim_current.buffer.__setitem__.assert_not_called() - vim_current.buffer.options.__setitem__.assert_not_called() + with patch( 'vim.buffers', vim_buffers ): + vimsupport.ReplaceChunks( chunks ) + # We checked for the right file names + get_buffer_number_for_filename.assert_has_exact_calls( [ + call( first_buffer_name ), + call( second_buffer_name ), + call( first_buffer_name ), + call( second_buffer_name ), + call( second_buffer_name ), + ] ) -def BufferIsVisibleForFilename_test(): - visible_buffer = VimBuffer( 'visible_filename', number = 1 ) - hidden_buffer = VimBuffer( 'hidden_filename', number = 2 ) + # We checked if it was OK to open the file + confirm.assert_has_exact_calls( [ + call( vimsupport.FIXIT_OPENING_BUFFERS_MESSAGE_FORMAT.format( 1 ) ) + ] ) - with MockVimBuffers( [ visible_buffer, hidden_buffer ], [ visible_buffer ] ): - assert_that( vimsupport.BufferIsVisibleForFilename( 'visible_filename' ) ) - assert_that( - not vimsupport.BufferIsVisibleForFilename( 'hidden_filename' ) ) - assert_that( - not vimsupport.BufferIsVisibleForFilename( 'another_filename' ) ) + # Ensure that buffers are updated + assert_that( second_file.GetLines(), contains_exactly( + 'another line1', + 'second_file_replacement ACME line2', + ) ) + assert_that( first_file.GetLines(), contains_exactly( + 'first_file_replacement line2', + 'line3', + ) ) + # We open '2_second_file' as expected. + open_filename.assert_called_with( second_buffer_name, { + 'focus': True, + 'fix': True, + 'size': 10 + } ) -def CloseBuffersForFilename_test(): - current_buffer = VimBuffer( 'some_filename', number = 2 ) - other_buffer = VimBuffer( 'some_filename', number = 5 ) + # And close it again, then show the quickfix window. + vim_command.assert_has_exact_calls( [ + call( 'lclose' ), + call( 'hide' ), + ] ) - with MockVimBuffers( [ current_buffer, other_buffer ], - [ current_buffer ] ) as vim: - vimsupport.CloseBuffersForFilename( 'some_filename' ) + qflist = json.dumps( [ { + 'bufnr': 22, + 'filename': first_buffer_name, + 'lnum': 1, + 'col': 1, + 'text': 'first_file_replacement ', + 'type': 'F' + }, { + 'bufnr': 19, + 'filename': second_buffer_name, + 'lnum': 2, + 'col': 1, + 'text': 'second_file_replacement ', + 'type': 'F' + } ] ) + # And update the quickfix list with each entry + vim_eval.assert_has_exact_calls( [ + call( '&previewheight' ), + call( f'setqflist( { qflist } )' ) + ] ) - assert_that( vim.buffers, empty() ) + # And it is ReplaceChunks that prints the message showing the number of + # changes + post_vim_message.assert_has_exact_calls( [ + call( 'Applied 2 changes', warning = False ), + ] ) -@patch( 'vim.command', new_callable = ExtendedMock ) -@patch( 'vim.current', new_callable = ExtendedMock ) -def OpenFilename_test( vim_current, vim_command ): - # Options used to open a logfile. - options = { - 'size': vimsupport.GetIntValue( '&previewheight' ), - 'fix': True, - 'focus': False, - 'watch': True, - 'position': 'end' - } + def test_GetDiagnosticMatchPattern_ErrorInMiddleOfLine( self ): + current_buffer = VimBuffer( + 'some_file', + contents = [ 'Highlight this error please' ] + ) - vimsupport.OpenFilename( __file__, options ) + with patch( 'vim.current.buffer', current_buffer ): + assert_that( + vimsupport.GetDiagnosticMatchPattern( 1, 16, 1, 21 ), + equal_to( '\\%1l\\%16c\\_.\\{-}\\%1l\\%21c' ) + ) - vim_command.assert_has_exact_calls( [ - call( f'12split { __file__ }' ), - call( f"exec 'au BufEnter :silent! checktime { __file__ }'" ), - call( 'silent! normal! Gzz' ), - call( 'silent! wincmd p' ) - ] ) - vim_current.buffer.options.__setitem__.assert_has_exact_calls( [ - call( 'autoread', True ), - ] ) + def test_AddDiagnosticSyntaxMatch_WarningAtEndOfLine( self ): + current_buffer = VimBuffer( + 'some_file', + contents = [ 'Highlight this warning' ] + ) - vim_current.window.options.__setitem__.assert_has_exact_calls( [ - call( 'winfixheight', True ) - ] ) + with patch( 'vim.current.buffer', current_buffer ): + assert_that( + vimsupport.GetDiagnosticMatchPattern( 1, 16, 1, 23 ), + equal_to( '\\%1l\\%16c\\_.\\{-}\\%1l\\%23c' ) + ) -def GetUnsavedAndSpecifiedBufferData_EncodedUnicodeCharsInBuffers_test(): - filepath = os.path.realpath( 'filename' ) - contents = [ ToBytes( 'abc' ), ToBytes( 'fДa' ) ] - vim_buffer = VimBuffer( filepath, contents = contents ) + def test_AddDiagnosticSyntaxMatch_UnicodeAtEndOfLine( self ): + current_buffer = VimBuffer( + 'some_file', + contents = [ 'Highlight unicøde' ] + ) - with patch( 'vim.buffers', [ vim_buffer ] ): - assert_that( vimsupport.GetUnsavedAndSpecifiedBufferData( vim_buffer, - filepath ), - has_entry( filepath, - has_entry( 'contents', 'abc\nfДa\n' ) ) ) + with patch( 'vim.current.buffer', current_buffer ): + assert_that( + vimsupport.GetDiagnosticMatchPattern( 1, 16, 1, 19 ), + equal_to( '\\%1l\\%16c\\_.\\{-}\\%1l\\%19c' ) + ) -def GetBufferFilepath_NoBufferName_UnicodeWorkingDirectory_test(): - vim_buffer = VimBuffer( '', number = 42 ) - unicode_dir = PathToTestFile( 'uni¢od€' ) - with CurrentWorkingDirectory( unicode_dir ): - assert_that( vimsupport.GetBufferFilepath( vim_buffer ), - equal_to( os.path.join( unicode_dir, '42' ) ) ) + def test_AddDiagnosticSyntaxMatch_NonPositivePosition( self ): + current_buffer = VimBuffer( + 'some_file', + contents = [ 'Some contents' ] + ) + with patch( 'vim.current.buffer', current_buffer ): + assert_that( + vimsupport.GetDiagnosticMatchPattern( 0, 0, 0, 0 ), + equal_to( '\\%1l\\%1c\\_.\\{-}\\%1l\\%1c' ) + ) -# NOTE: Vim returns byte offsets for columns, not actual character columns. This -# makes 'ДД' have 4 columns: column 0, column 2 and column 4. -@patch( 'vim.current.line', ToBytes( 'ДДaa' ) ) -@patch( 'ycm.vimsupport.CurrentColumn', side_effect = [ 4 ] ) -def TextBeforeCursor_EncodedUnicode_test( *args ): - assert_that( vimsupport.TextBeforeCursor(), equal_to( 'ДД' ) ) + assert_that( + vimsupport.GetDiagnosticMatchPattern( -1, -2, -3, -4 ), + equal_to( '\\%1l\\%1c\\_.\\{-}\\%1l\\%1c' ) + ) -# NOTE: Vim returns byte offsets for columns, not actual character columns. This -# makes 'ДД' have 4 columns: column 0, column 2 and column 4. -@patch( 'vim.current.line', ToBytes( 'aaДД' ) ) -@patch( 'ycm.vimsupport.CurrentColumn', side_effect = [ 2 ] ) -def TextAfterCursor_EncodedUnicode_test( *args ): - assert_that( vimsupport.TextAfterCursor(), equal_to( 'ДД' ) ) + @patch( 'vim.command', new_callable=ExtendedMock ) + @patch( 'vim.current', new_callable=ExtendedMock ) + def test_WriteToPreviewWindow( self, vim_current, vim_command ): + vim_current.window.options.__getitem__ = MagicMock( return_value = True ) + vimsupport.WriteToPreviewWindow( "test" ) -@patch( 'vim.current.line', ToBytes( 'fДa' ) ) -def CurrentLineContents_EncodedUnicode_test( *args ): - assert_that( vimsupport.CurrentLineContents(), equal_to( 'fДa' ) ) + vim_command.assert_has_exact_calls( [ + call( 'silent! pclose!' ), + call( 'silent! pedit! _TEMP_FILE_' ), + call( 'silent! wincmd P' ), + call( 'silent! wincmd p' ) ] ) + vim_current.buffer.__setitem__.assert_called_with( + slice( None, None, None ), [ 'test' ] ) -@patch( 'vim.eval', side_effect = lambda x: x ) -def VimExpressionToPythonType_IntAsUnicode_test( *args ): - assert_that( vimsupport.VimExpressionToPythonType( '123' ), equal_to( 123 ) ) + vim_current.buffer.options.__setitem__.assert_has_exact_calls( [ + call( 'modifiable', True ), + call( 'readonly', False ), + call( 'buftype', 'nofile' ), + call( 'bufhidden', 'wipe' ), + call( 'buflisted', False ), + call( 'swapfile', False ), + call( 'modifiable', False ), + call( 'modified', False ), + call( 'readonly', True ), + ], any_order = True ) -@patch( 'vim.eval', side_effect = lambda x: x ) -def VimExpressionToPythonType_IntAsBytes_test( *args ): - assert_that( vimsupport.VimExpressionToPythonType( ToBytes( '123' ) ), - equal_to( 123 ) ) + @patch( 'vim.current' ) + def test_WriteToPreviewWindow_MultiLine( self, vim_current ): + vim_current.window.options.__getitem__ = MagicMock( return_value = True ) + vimsupport.WriteToPreviewWindow( "test\ntest2" ) + vim_current.buffer.__setitem__.assert_called_with( + slice( None, None, None ), [ 'test', 'test2' ] ) -@patch( 'vim.eval', side_effect = lambda x: x ) -def VimExpressionToPythonType_StringAsUnicode_test( *args ): - assert_that( vimsupport.VimExpressionToPythonType( 'foo' ), - equal_to( 'foo' ) ) + @patch( 'vim.command', new_callable=ExtendedMock ) + @patch( 'vim.current', new_callable=ExtendedMock ) + def test_WriteToPreviewWindow_JumpFail( self, vim_current, vim_command ): + vim_current.window.options.__getitem__ = MagicMock( return_value = False ) -@patch( 'vim.eval', side_effect = lambda x: x ) -def VimExpressionToPythonType_StringAsBytes_test( *args ): - assert_that( vimsupport.VimExpressionToPythonType( ToBytes( 'foo' ) ), - equal_to( 'foo' ) ) + vimsupport.WriteToPreviewWindow( "test" ) + vim_command.assert_has_exact_calls( [ + call( 'silent! pclose!' ), + call( 'silent! pedit! _TEMP_FILE_' ), + call( 'silent! wincmd P' ), + call( 'redraw' ), + call( "echo 'test'" ), + ] ) -@patch( 'vim.eval', side_effect = lambda x: x ) -def VimExpressionToPythonType_ListPassthrough_test( *args ): - assert_that( vimsupport.VimExpressionToPythonType( [ 1, 2 ] ), - equal_to( [ 1, 2 ] ) ) + vim_current.buffer.__setitem__.assert_not_called() + vim_current.buffer.options.__setitem__.assert_not_called() -@patch( 'vim.eval', side_effect = lambda x: x ) -def VimExpressionToPythonType_ObjectPassthrough_test( *args ): - assert_that( vimsupport.VimExpressionToPythonType( { 1: 2 } ), - equal_to( { 1: 2 } ) ) + @patch( 'vim.command', new_callable=ExtendedMock ) + @patch( 'vim.current', new_callable=ExtendedMock ) + def test_WriteToPreviewWindow_JumpFail_MultiLine( + self, vim_current, vim_command ): + vim_current.window.options.__getitem__ = MagicMock( return_value = False ) -@patch( 'vim.eval', side_effect = lambda x: x ) -def VimExpressionToPythonType_GeneratorPassthrough_test( *args ): - gen = ( x**2 for x in [ 1, 2, 3 ] ) - assert_that( vimsupport.VimExpressionToPythonType( gen ), equal_to( gen ) ) + vimsupport.WriteToPreviewWindow( "test\ntest2" ) + vim_command.assert_has_exact_calls( [ + call( 'silent! pclose!' ), + call( 'silent! pedit! _TEMP_FILE_' ), + call( 'silent! wincmd P' ), + call( 'redraw' ), + call( "echo 'test'" ), + call( "echo 'test2'" ), + ] ) -@patch( 'vim.eval', - new_callable = ExtendedMock, - side_effect = [ None, 2, None ] ) -def SelectFromList_LastItem_test( vim_eval ): - assert_that( vimsupport.SelectFromList( 'test', [ 'a', 'b' ] ), - equal_to( 1 ) ) + vim_current.buffer.__setitem__.assert_not_called() + vim_current.buffer.options.__setitem__.assert_not_called() - vim_eval.assert_has_exact_calls( [ - call( 'inputsave()' ), - call( 'inputlist( ["test", "1: a", "2: b"] )' ), - call( 'inputrestore()' ) - ] ) + def test_BufferIsVisibleForFilename( self ): + visible_buffer = VimBuffer( 'visible_filename', number = 1 ) + hidden_buffer = VimBuffer( 'hidden_filename', number = 2 ) -@patch( 'vim.eval', - new_callable = ExtendedMock, - side_effect = [ None, 1, None ] ) -def SelectFromList_FirstItem_test( vim_eval ): - assert_that( vimsupport.SelectFromList( 'test', [ 'a', 'b' ] ), - equal_to( 0 ) ) + with MockVimBuffers( [ visible_buffer, hidden_buffer ], + [ visible_buffer ] ): + assert_that( vimsupport.BufferIsVisibleForFilename( 'visible_filename' ) ) + assert_that( + not vimsupport.BufferIsVisibleForFilename( 'hidden_filename' ) ) + assert_that( + not vimsupport.BufferIsVisibleForFilename( 'another_filename' ) ) - vim_eval.assert_has_exact_calls( [ - call( 'inputsave()' ), - call( 'inputlist( ["test", "1: a", "2: b"] )' ), - call( 'inputrestore()' ) - ] ) + def test_CloseBuffersForFilename( self ): + current_buffer = VimBuffer( 'some_filename', number = 2 ) + other_buffer = VimBuffer( 'some_filename', number = 5 ) -@patch( 'vim.eval', side_effect = [ None, 3, None ] ) -def SelectFromList_OutOfRange_test( vim_eval ): - assert_that( calling( vimsupport.SelectFromList ).with_args( 'test', - [ 'a', 'b' ] ), - raises( RuntimeError, vimsupport.NO_SELECTION_MADE_MSG ) ) + with MockVimBuffers( [ current_buffer, other_buffer ], + [ current_buffer ] ) as vim: + vimsupport.CloseBuffersForFilename( 'some_filename' ) + assert_that( vim.buffers, empty() ) -@patch( 'vim.eval', side_effect = [ None, 0, None ] ) -def SelectFromList_SelectPrompt_test( vim_eval ): - assert_that( calling( vimsupport.SelectFromList ).with_args( 'test', - [ 'a', 'b' ] ), - raises( RuntimeError, vimsupport.NO_SELECTION_MADE_MSG ) ) + @patch( 'vim.command', new_callable = ExtendedMock ) + @patch( 'vim.current', new_callable = ExtendedMock ) + def test_OpenFilename( self, vim_current, vim_command ): + # Options used to open a logfile. + options = { + 'size': vimsupport.GetIntValue( '&previewheight' ), + 'fix': True, + 'focus': False, + 'watch': True, + 'position': 'end' + } + + vimsupport.OpenFilename( __file__, options ) -@patch( 'vim.eval', side_effect = [ None, -199, None ] ) -def SelectFromList_Negative_test( vim_eval ): - assert_that( calling( vimsupport.SelectFromList ).with_args( 'test', - [ 'a', 'b' ] ), - raises( RuntimeError, vimsupport.NO_SELECTION_MADE_MSG ) ) - - -def Filetypes_IntegerFiletype_test(): - current_buffer = VimBuffer( 'buffer', number = 1, filetype = '42' ) - with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): - assert_that( vimsupport.CurrentFiletypes(), contains_exactly( '42' ) ) - assert_that( vimsupport.GetBufferFiletypes( 1 ), contains_exactly( '42' ) ) - assert_that( vimsupport.FiletypesForBuffer( current_buffer ), - contains_exactly( '42' ) ) - - -@patch( 'ycm.vimsupport.VariableExists', return_value = False ) -@patch( 'ycm.vimsupport.SearchInCurrentBuffer', return_value = 0 ) -@patch( 'vim.current' ) -def InsertNamespace_insert_test( vim_current, *args ): - contents = [ '', - 'namespace Taqueria {', - '', - ' int taco = Math' ] - vim_current.buffer = VimBuffer( '', contents = contents ) - vim_current.window.cursor = ( 1, 1 ) - - vimsupport.InsertNamespace( 'System' ) - - expected_buffer = [ 'using System;', - '', - 'namespace Taqueria {', - '', - ' int taco = Math' ] - AssertBuffersAreEqualAsBytes( expected_buffer, vim_current.buffer ) - - -@patch( 'ycm.vimsupport.VariableExists', return_value = False ) -@patch( 'ycm.vimsupport.SearchInCurrentBuffer', return_value = 2 ) -@patch( 'vim.current' ) -def InsertNamespace_append_test( vim_current, *args ): - contents = [ 'namespace Taqueria {', - ' using System;', - '', - ' class Tasty {', - ' int taco;', - ' List salad = new List' ] - vim_current.buffer = VimBuffer( '', contents = contents ) - vim_current.window.cursor = ( 1, 1 ) - - vimsupport.InsertNamespace( 'System.Collections' ) - - expected_buffer = [ 'namespace Taqueria {', - ' using System;', - ' using System.Collections;', - '', - ' class Tasty {', - ' int taco;', - ' List salad = new List' ] - AssertBuffersAreEqualAsBytes( expected_buffer, vim_current.buffer ) - - -@patch( 'vim.command', new_callable = ExtendedMock ) -def JumpToLocation_SameFile_SameBuffer_NoSwapFile_test( vim_command ): - current_buffer = VimBuffer( 'uni¢𐍈d€' ) - with MockVimBuffers( [ current_buffer ], [ current_buffer ] ) as vim: - vimsupport.JumpToLocation( os.path.realpath( 'uni¢𐍈d€' ), - 2, - 5, - 'aboveleft', - 'same-buffer' ) - - assert_that( vim.current.window.cursor, equal_to( ( 2, 4 ) ) ) vim_command.assert_has_exact_calls( [ - call( 'normal! m\'' ), - call( 'normal! zz' ) + call( f'12split { __file__ }' ), + call( f"exec 'au BufEnter :silent! checktime { __file__ }'" ), + call( 'silent! normal! Gzz' ), + call( 'silent! wincmd p' ) ] ) + vim_current.buffer.options.__setitem__.assert_has_exact_calls( [ + call( 'autoread', True ), + ] ) -@patch( 'vim.command', new_callable = ExtendedMock ) -def JumpToLocation_DifferentFile_SameBuffer_Unmodified_test( vim_command ): - current_buffer = VimBuffer( 'uni¢𐍈d€' ) - with MockVimBuffers( [ current_buffer ], [ current_buffer ] ) as vim: - target_name = os.path.realpath( 'different_uni¢𐍈d€' ) + vim_current.window.options.__setitem__.assert_has_exact_calls( [ + call( 'winfixheight', True ) + ] ) - vimsupport.JumpToLocation( target_name, 2, 5, 'belowright', 'same-buffer' ) - assert_that( vim.current.window.cursor, equal_to( ( 2, 4 ) ) ) - vim_command.assert_has_exact_calls( [ - call( 'normal! m\'' ), - call( f'keepjumps belowright edit { target_name }' ), - call( 'normal! zz' ) - ] ) + def test_GetUnsavedAndSpecifiedBufferData_EncodedUnicodeCharsInBuffers( + self ): + filepath = os.path.realpath( 'filename' ) + contents = [ ToBytes( 'abc' ), ToBytes( 'fДa' ) ] + vim_buffer = VimBuffer( filepath, contents = contents ) + with patch( 'vim.buffers', [ vim_buffer ] ): + assert_that( vimsupport.GetUnsavedAndSpecifiedBufferData( vim_buffer, + filepath ), + has_entry( filepath, + has_entry( 'contents', 'abc\nfДa\n' ) ) ) -@patch( 'vim.command', new_callable = ExtendedMock ) -def JumpToLocation_DifferentFile_SameBuffer_Modified_CannotHide_test( - vim_command ): - current_buffer = VimBuffer( 'uni¢𐍈d€', modified = True ) - with MockVimBuffers( [ current_buffer ], [ current_buffer ] ) as vim: - target_name = os.path.realpath( 'different_uni¢𐍈d€' ) + def test_GetBufferFilepath_NoBufferName_UnicodeWorkingDirectory( self ): + vim_buffer = VimBuffer( '', number = 42 ) + unicode_dir = PathToTestFile( 'uni¢od€' ) + with CurrentWorkingDirectory( unicode_dir ): + assert_that( vimsupport.GetBufferFilepath( vim_buffer ), + equal_to( os.path.join( unicode_dir, '42' ) ) ) - vimsupport.JumpToLocation( target_name, 2, 5, 'botright', 'same-buffer' ) - assert_that( vim.current.window.cursor, equal_to( ( 2, 4 ) ) ) - vim_command.assert_has_exact_calls( [ - call( 'normal! m\'' ), - call( f'keepjumps botright split { target_name }' ), - call( 'normal! zz' ) - ] ) + # NOTE: Vim returns byte offsets for columns, not actual character columns. + # This makes 'ДД' have 4 columns: column 0, column 2 and column 4. + @patch( 'vim.current.line', ToBytes( 'ДДaa' ) ) + @patch( 'ycm.vimsupport.CurrentColumn', side_effect = [ 4 ] ) + def test_TextBeforeCursor_EncodedUnicode( *args ): + assert_that( vimsupport.TextBeforeCursor(), equal_to( 'ДД' ) ) -@patch( 'vim.command', new_callable = ExtendedMock ) -def JumpToLocation_DifferentFile_SameBuffer_Modified_CanHide_test( - vim_command ): + # NOTE: Vim returns byte offsets for columns, not actual character columns. + # This makes 'ДД' have 4 columns: column 0, column 2 and column 4. + @patch( 'vim.current.line', ToBytes( 'aaДД' ) ) + @patch( 'ycm.vimsupport.CurrentColumn', side_effect = [ 2 ] ) + def test_TextAfterCursor_EncodedUnicode( *args ): + assert_that( vimsupport.TextAfterCursor(), equal_to( 'ДД' ) ) - current_buffer = VimBuffer( 'uni¢𐍈d€', modified = True, bufhidden = "hide" ) - with MockVimBuffers( [ current_buffer ], [ current_buffer ] ) as vim: - target_name = os.path.realpath( 'different_uni¢𐍈d€' ) - vimsupport.JumpToLocation( target_name, 2, 5, 'leftabove', 'same-buffer' ) + @patch( 'vim.current.line', ToBytes( 'fДa' ) ) + def test_CurrentLineContents_EncodedUnicode( *args ): + assert_that( vimsupport.CurrentLineContents(), equal_to( 'fДa' ) ) - assert_that( vim.current.window.cursor, equal_to( ( 2, 4 ) ) ) - vim_command.assert_has_exact_calls( [ - call( 'normal! m\'' ), - call( f'keepjumps leftabove edit { target_name }' ), - call( 'normal! zz' ) - ] ) + @patch( 'vim.eval', side_effect = lambda x: x ) + def test_VimExpressionToPythonType_IntAsUnicode( *args ): + assert_that( vimsupport.VimExpressionToPythonType( '123' ), + equal_to( 123 ) ) -@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( 'different_uni¢𐍈d€' ), - 2, - 5, - 'rightbelow', - 'same-buffer' ), - raises( VimError, 'Unknown code' ) - ) + @patch( 'vim.eval', side_effect = lambda x: x ) + def test_VimExpressionToPythonType_IntAsBytes( *args ): + assert_that( vimsupport.VimExpressionToPythonType( ToBytes( '123' ) ), + equal_to( 123 ) ) -@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( 'different_uni¢𐍈d€' ) - vimsupport.JumpToLocation( target_name, 2, 5, 'topleft', 'same-buffer' ) + @patch( 'vim.eval', side_effect = lambda x: x ) + def test_VimExpressionToPythonType_StringAsUnicode( *args ): + assert_that( vimsupport.VimExpressionToPythonType( 'foo' ), + equal_to( 'foo' ) ) - vim_command.assert_has_exact_calls( [ - call( 'normal! m\'' ), - call( f'keepjumps topleft edit { target_name }' ) - ] ) + @patch( 'vim.eval', side_effect = lambda x: x ) + def test_VimExpressionToPythonType_StringAsBytes( *args ): + assert_that( vimsupport.VimExpressionToPythonType( ToBytes( 'foo' ) ), + equal_to( 'foo' ) ) -@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( 'different_uni¢𐍈d€' ) - vimsupport.JumpToLocation( target_name, 2, 5, 'vertical', 'same-buffer' ) + @patch( 'vim.eval', side_effect = lambda x: x ) + def test_VimExpressionToPythonType_ListPassthrough( *args ): + assert_that( vimsupport.VimExpressionToPythonType( [ 1, 2 ] ), + equal_to( [ 1, 2 ] ) ) - vim_command.assert_has_exact_calls( [ - call( 'normal! m\'' ), - call( f'keepjumps vertical edit { target_name }' ) - ] ) + @patch( 'vim.eval', side_effect = lambda x: x ) + def test_VimExpressionToPythonType_ObjectPassthrough( *args ): + assert_that( vimsupport.VimExpressionToPythonType( { 1: 2 } ), + equal_to( { 1: 2 } ) ) -@patch( 'vim.command', new_callable = ExtendedMock ) -def JumpToLocation_DifferentFile_Split_CurrentTab_NotAlreadyOpened_test( - vim_command ): - current_buffer = VimBuffer( 'uni¢𐍈d€' ) - current_window = MagicMock( buffer = current_buffer ) - current_tab = MagicMock( windows = [ current_window ] ) - with MockVimBuffers( [ current_buffer ], [ current_buffer ] ) as vim: - vim.current.tabpage = current_tab + @patch( 'vim.eval', side_effect = lambda x: x ) + def test_VimExpressionToPythonType_GeneratorPassthrough( *args ): + gen = ( x**2 for x in [ 1, 2, 3 ] ) + assert_that( vimsupport.VimExpressionToPythonType( gen ), equal_to( gen ) ) - target_name = os.path.realpath( 'different_uni¢𐍈d€' ) - vimsupport.JumpToLocation( target_name, - 2, - 5, - 'aboveleft', - 'split-or-existing-window' ) + @patch( 'vim.eval', + new_callable = ExtendedMock, + side_effect = [ None, 2, None ] ) + def test_SelectFromList_LastItem( self, vim_eval ): + assert_that( vimsupport.SelectFromList( 'test', [ 'a', 'b' ] ), + equal_to( 1 ) ) - vim_command.assert_has_exact_calls( [ - call( 'normal! m\'' ), - call( f'keepjumps aboveleft split { target_name }' ), - call( 'normal! zz' ) + vim_eval.assert_has_exact_calls( [ + call( 'inputsave()' ), + call( 'inputlist( ["test", "1: a", "2: b"] )' ), + call( 'inputrestore()' ) ] ) -@patch( 'vim.command', new_callable = ExtendedMock ) -def JumpToLocation_DifferentFile_Split_CurrentTab_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 MockVimBuffers( [ current_buffer, different_buffer ], - [ current_buffer ] ) as vim: - vim.current.tabpage = current_tab - - vimsupport.JumpToLocation( os.path.realpath( 'different_uni¢𐍈d€' ), - 2, - 5, - 'belowright', - 'split-or-existing-window' ) - - 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' ) + @patch( 'vim.eval', + new_callable = ExtendedMock, + side_effect = [ None, 1, None ] ) + def test_SelectFromList_FirstItem( self, vim_eval ): + assert_that( vimsupport.SelectFromList( 'test', [ 'a', 'b' ] ), + equal_to( 0 ) ) + + vim_eval.assert_has_exact_calls( [ + call( 'inputsave()' ), + call( 'inputlist( ["test", "1: a", "2: b"] )' ), + call( 'inputrestore()' ) ] ) -@WindowsAndMacOnly -@patch( 'vim.command', new_callable = ExtendedMock ) -def JumpToLocation_DifferentFile_Split_CurrentTab_AlreadyOpened_Case_test( - vim_command ): - - current_buffer = VimBuffer( 'current_buffer' ) - different_buffer = VimBuffer( 'AnotHer_buFfeR' ) - current_window = MagicMock( buffer = current_buffer ) - different_window = MagicMock( buffer = different_buffer ) - current_tab = MagicMock( windows = [ current_window, different_window ] ) - with MockVimBuffers( [ current_buffer, different_buffer ], - [ current_buffer ] ) as vim: - vim.current.tabpage = current_tab - - vimsupport.JumpToLocation( os.path.realpath( 'anOther_BuffEr' ), - 4, - 1, - 'belowright', - 'split-or-existing-window' ) - - 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( ( 4, 0 ) ) ) - vim_command.assert_has_exact_calls( [ - call( 'normal! m\'' ), - call( 'normal! zz' ) - ] ) + @patch( 'vim.eval', side_effect = [ None, 3, None ] ) + def test_SelectFromList_OutOfRange( self, vim_eval ): + assert_that( calling( vimsupport.SelectFromList ).with_args( 'test', + [ 'a', 'b' ] ), + raises( RuntimeError, vimsupport.NO_SELECTION_MADE_MSG ) ) -@patch( 'vim.command', new_callable = ExtendedMock ) -def JumpToLocation_DifferentFile_Split_AllTabs_NotAlreadyOpened_test( - vim_command ): + @patch( 'vim.eval', side_effect = [ None, 0, None ] ) + def test_SelectFromList_SelectPrompt( self, vim_eval ): + assert_that( calling( vimsupport.SelectFromList ).with_args( 'test', + [ 'a', 'b' ] ), + raises( RuntimeError, vimsupport.NO_SELECTION_MADE_MSG ) ) + + + @patch( 'vim.eval', side_effect = [ None, -199, None ] ) + def test_SelectFromList_Negative( self, vim_eval ): + assert_that( calling( vimsupport.SelectFromList ).with_args( 'test', + [ 'a', 'b' ] ), + raises( RuntimeError, vimsupport.NO_SELECTION_MADE_MSG ) ) + + + def test_Filetypes_IntegerFiletype( self ): + current_buffer = VimBuffer( 'buffer', number = 1, filetype = '42' ) + with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): + assert_that( vimsupport.CurrentFiletypes(), contains_exactly( '42' ) ) + assert_that( vimsupport.GetBufferFiletypes( 1 ), + contains_exactly( '42' ) ) + assert_that( vimsupport.FiletypesForBuffer( current_buffer ), + contains_exactly( '42' ) ) + + + @patch( 'ycm.vimsupport.VariableExists', return_value = False ) + @patch( 'ycm.vimsupport.SearchInCurrentBuffer', return_value = 0 ) + @patch( 'vim.current' ) + def test_InsertNamespace_insert( self, vim_current, *args ): + contents = [ '', + 'namespace Taqueria {', + '', + ' int taco = Math' ] + vim_current.buffer = VimBuffer( '', contents = contents ) + vim_current.window.cursor = ( 1, 1 ) + + vimsupport.InsertNamespace( 'System' ) + + expected_buffer = [ 'using System;', + '', + 'namespace Taqueria {', + '', + ' int taco = Math' ] + AssertBuffersAreEqualAsBytes( expected_buffer, vim_current.buffer ) + + + @patch( 'ycm.vimsupport.VariableExists', return_value = False ) + @patch( 'ycm.vimsupport.SearchInCurrentBuffer', return_value = 2 ) + @patch( 'vim.current' ) + def test_InsertNamespace_append( self, vim_current, *args ): + contents = [ 'namespace Taqueria {', + ' using System;', + '', + ' class Tasty {', + ' int taco;', + ' List salad = new List' ] + vim_current.buffer = VimBuffer( '', contents = contents ) + vim_current.window.cursor = ( 1, 1 ) + + vimsupport.InsertNamespace( 'System.Collections' ) + + expected_buffer = [ 'namespace Taqueria {', + ' using System;', + ' using System.Collections;', + '', + ' class Tasty {', + ' int taco;', + ' List salad = new List' ] + AssertBuffersAreEqualAsBytes( expected_buffer, vim_current.buffer ) + + + @patch( 'vim.command', new_callable = ExtendedMock ) + def test_JumpToLocation_SameFile_SameBuffer_NoSwapFile( self, vim_command ): + current_buffer = VimBuffer( 'uni¢𐍈d€' ) + with MockVimBuffers( [ current_buffer ], [ current_buffer ] ) as vim: + vimsupport.JumpToLocation( os.path.realpath( 'uni¢𐍈d€' ), + 2, + 5, + 'aboveleft', + 'same-buffer' ) - current_buffer = VimBuffer( 'uni¢𐍈d€' ) - with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): - target_name = os.path.realpath( 'different_uni¢𐍈d€' ) + assert_that( vim.current.window.cursor, equal_to( ( 2, 4 ) ) ) + vim_command.assert_has_exact_calls( [ + call( 'normal! m\'' ), + call( 'normal! zz' ) + ] ) - vimsupport.JumpToLocation( target_name, - 2, - 5, - 'tab', - 'split-or-existing-window' ) - vim_command.assert_has_exact_calls( [ - call( 'normal! m\'' ), - call( f'keepjumps tab split { target_name }' ), - call( 'normal! zz' ) - ] ) + @patch( 'vim.command', new_callable = ExtendedMock ) + def test_JumpToLocation_DifferentFile_SameBuffer_Unmodified( + self, vim_command ): + current_buffer = VimBuffer( 'uni¢𐍈d€' ) + with MockVimBuffers( [ current_buffer ], [ current_buffer ] ) as vim: + target_name = os.path.realpath( 'different_uni¢𐍈d€' ) + + vimsupport.JumpToLocation( target_name, + 2, + 5, + 'belowright', + 'same-buffer' ) + + assert_that( vim.current.window.cursor, equal_to( ( 2, 4 ) ) ) + vim_command.assert_has_exact_calls( [ + call( 'normal! m\'' ), + call( f'keepjumps belowright edit { target_name }' ), + call( 'normal! zz' ) + ] ) + + + @patch( 'vim.command', new_callable = ExtendedMock ) + def test_JumpToLocation_DifferentFile_SameBuffer_Modified_CannotHide( + self, vim_command ): + + current_buffer = VimBuffer( 'uni¢𐍈d€', modified = True ) + with MockVimBuffers( [ current_buffer ], [ current_buffer ] ) as vim: + target_name = os.path.realpath( 'different_uni¢𐍈d€' ) + + vimsupport.JumpToLocation( target_name, 2, 5, 'botright', 'same-buffer' ) + + assert_that( vim.current.window.cursor, equal_to( ( 2, 4 ) ) ) + vim_command.assert_has_exact_calls( [ + call( 'normal! m\'' ), + call( f'keepjumps botright split { target_name }' ), + call( 'normal! zz' ) + ] ) + + + @patch( 'vim.command', new_callable = ExtendedMock ) + def test_JumpToLocation_DifferentFile_SameBuffer_Modified_CanHide( + self, vim_command ): + + current_buffer = VimBuffer( 'uni¢𐍈d€', modified = True, bufhidden = "hide" ) + with MockVimBuffers( [ current_buffer ], [ current_buffer ] ) as vim: + target_name = os.path.realpath( 'different_uni¢𐍈d€' ) + + vimsupport.JumpToLocation( target_name, 2, 5, 'leftabove', 'same-buffer' ) + + assert_that( vim.current.window.cursor, equal_to( ( 2, 4 ) ) ) + vim_command.assert_has_exact_calls( [ + call( 'normal! m\'' ), + call( f'keepjumps leftabove edit { target_name }' ), + call( 'normal! zz' ) + ] ) + + + @patch( 'vim.command', + side_effect = [ None, VimError( 'Unknown code' ), None ] ) + def test_JumpToLocation_DifferentFile_SameBuffer_SwapFile_Unexpected( + self, vim_command ): + + current_buffer = VimBuffer( 'uni¢𐍈d€' ) + with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): + assert_that( + calling( vimsupport.JumpToLocation ).with_args( + os.path.realpath( 'different_uni¢𐍈d€' ), + 2, + 5, + 'rightbelow', + 'same-buffer' ), + raises( VimError, 'Unknown code' ) + ) + + + @patch( 'vim.command', + new_callable = ExtendedMock, + side_effect = [ None, VimError( 'E325' ), None ] ) + def test_JumpToLocation_DifferentFile_SameBuffer_SwapFile_Quit( + self, vim_command ): + current_buffer = VimBuffer( 'uni¢𐍈d€' ) + with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): + target_name = os.path.realpath( 'different_uni¢𐍈d€' ) + + vimsupport.JumpToLocation( target_name, 2, 5, 'topleft', 'same-buffer' ) + + vim_command.assert_has_exact_calls( [ + call( 'normal! m\'' ), + call( f'keepjumps topleft edit { target_name }' ) + ] ) + + + @patch( 'vim.command', + new_callable = ExtendedMock, + side_effect = [ None, KeyboardInterrupt, None ] ) + def test_JumpToLocation_DifferentFile_SameBuffer_SwapFile_Abort( + self, vim_command ): + current_buffer = VimBuffer( 'uni¢𐍈d€' ) + with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): + target_name = os.path.realpath( 'different_uni¢𐍈d€' ) + + vimsupport.JumpToLocation( target_name, 2, 5, 'vertical', 'same-buffer' ) + + vim_command.assert_has_exact_calls( [ + call( 'normal! m\'' ), + call( f'keepjumps vertical edit { target_name }' ) + ] ) + + + @patch( 'vim.command', new_callable = ExtendedMock ) + def test_JumpToLocation_DifferentFile_Split_CurrentTab_NotAlreadyOpened( + self, vim_command ): + + current_buffer = VimBuffer( 'uni¢𐍈d€' ) + current_window = MagicMock( buffer = current_buffer ) + current_tab = MagicMock( windows = [ current_window ] ) + with MockVimBuffers( [ current_buffer ], [ current_buffer ] ) as vim: + vim.current.tabpage = current_tab + + target_name = os.path.realpath( 'different_uni¢𐍈d€' ) + + vimsupport.JumpToLocation( target_name, + 2, + 5, + 'aboveleft', + 'split-or-existing-window' ) + + vim_command.assert_has_exact_calls( [ + call( 'normal! m\'' ), + call( f'keepjumps aboveleft split { target_name }' ), + call( 'normal! zz' ) + ] ) -@patch( 'vim.command', new_callable = ExtendedMock ) -def JumpToLocation_DifferentFile_Split_AllTabs_AlreadyOpened_test( - vim_command ): + @patch( 'vim.command', new_callable = ExtendedMock ) + def test_JumpToLocation_DifferentFile_Split_CurrentTab_AlreadyOpened( + self, 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 ] ): + 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 MockVimBuffers( [ current_buffer, different_buffer ], [ current_buffer ] ) as vim: + vim.current.tabpage = current_tab + vimsupport.JumpToLocation( os.path.realpath( 'different_uni¢𐍈d€' ), 2, 5, - 'tab', + 'belowright', 'split-or-existing-window' ) assert_that( vim.current.tabpage, equal_to( current_tab ) ) @@ -1963,58 +1913,135 @@ def JumpToLocation_DifferentFile_Split_AllTabs_AlreadyOpened_test( ] ) -@patch( 'vim.command', new_callable = ExtendedMock ) -def JumpToLocation_DifferentFile_NewOrExistingTab_NotAlreadyOpened_test( - vim_command ): + @WindowsAndMacOnly + @patch( 'vim.command', new_callable = ExtendedMock ) + def test_JumpToLocation_DifferentFile_Split_CurrentTab_AlreadyOpened_Case( + self, vim_command ): - current_buffer = VimBuffer( 'uni¢𐍈d€' ) - with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): - target_name = os.path.realpath( 'different_uni¢𐍈d€' ) + current_buffer = VimBuffer( 'current_buffer' ) + different_buffer = VimBuffer( 'AnotHer_buFfeR' ) + current_window = MagicMock( buffer = current_buffer ) + different_window = MagicMock( buffer = different_buffer ) + current_tab = MagicMock( windows = [ current_window, different_window ] ) + with MockVimBuffers( [ current_buffer, different_buffer ], + [ current_buffer ] ) as vim: + vim.current.tabpage = current_tab - vimsupport.JumpToLocation( target_name, - 2, - 5, - 'aboveleft vertical', - 'new-or-existing-tab' ) + vimsupport.JumpToLocation( os.path.realpath( 'anOther_BuffEr' ), + 4, + 1, + 'belowright', + 'split-or-existing-window' ) - vim_command.assert_has_exact_calls( [ - call( 'normal! m\'' ), - call( f'keepjumps aboveleft vertical tabedit { target_name }' ), - call( 'normal! zz' ) - ] ) + 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( ( 4, 0 ) ) ) + vim_command.assert_has_exact_calls( [ + call( 'normal! m\'' ), + call( 'normal! zz' ) + ] ) -@patch( 'vim.command', new_callable = ExtendedMock ) -def JumpToLocation_DifferentFile_NewOrExistingTab_AlreadyOpened_test( - vim_command ): + @patch( 'vim.command', new_callable = ExtendedMock ) + def test_JumpToLocation_DifferentFile_Split_AllTabs_NotAlreadyOpened( + self, 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( 'different_uni¢𐍈d€' ), + current_buffer = VimBuffer( 'uni¢𐍈d€' ) + with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): + target_name = os.path.realpath( 'different_uni¢𐍈d€' ) + + vimsupport.JumpToLocation( target_name, + 2, + 5, + 'tab', + 'split-or-existing-window' ) + + vim_command.assert_has_exact_calls( [ + call( 'normal! m\'' ), + call( f'keepjumps tab split { target_name }' ), + call( 'normal! zz' ) + ] ) + + + @patch( 'vim.command', new_callable = ExtendedMock ) + def test_JumpToLocation_DifferentFile_Split_AllTabs_AlreadyOpened( + self, 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( 'different_uni¢𐍈d€' ), + 2, + 5, + 'tab', + 'split-or-existing-window' ) + + 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' ) + ] ) + + + @patch( 'vim.command', new_callable = ExtendedMock ) + def test_JumpToLocation_DifferentFile_NewOrExistingTab_NotAlreadyOpened( + self, vim_command ): + + current_buffer = VimBuffer( 'uni¢𐍈d€' ) + with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): + target_name = os.path.realpath( 'different_uni¢𐍈d€' ) + + vimsupport.JumpToLocation( target_name, 2, 5, - 'belowright tab', + 'aboveleft vertical', 'new-or-existing-tab' ) - 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( f'keepjumps aboveleft vertical tabedit { target_name }' ), call( 'normal! zz' ) ] ) -@patch( 'ycm.tests.test_utils.VIM_VERSION', Version( 7, 4, 1578 ) ) -def VimVersionAtLeast_test(): - assert_that( vimsupport.VimVersionAtLeast( '7.3.414' ) ) - assert_that( vimsupport.VimVersionAtLeast( '7.4.1578' ) ) - assert_that( not vimsupport.VimVersionAtLeast( '7.4.1579' ) ) - assert_that( not vimsupport.VimVersionAtLeast( '7.4.1898' ) ) - assert_that( not vimsupport.VimVersionAtLeast( '8.1.278' ) ) + @patch( 'vim.command', new_callable = ExtendedMock ) + def test_JumpToLocation_DifferentFile_NewOrExistingTab_AlreadyOpened( + self, 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( 'different_uni¢𐍈d€' ), + 2, + 5, + 'belowright tab', + 'new-or-existing-tab' ) + + 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' ) + ] ) + + + @patch( 'ycm.tests.test_utils.VIM_VERSION', Version( 7, 4, 1578 ) ) + def test_VimVersionAtLeast( self ): + assert_that( vimsupport.VimVersionAtLeast( '7.3.414' ) ) + assert_that( vimsupport.VimVersionAtLeast( '7.4.1578' ) ) + assert_that( not vimsupport.VimVersionAtLeast( '7.4.1579' ) ) + assert_that( not vimsupport.VimVersionAtLeast( '7.4.1898' ) ) + assert_that( not vimsupport.VimVersionAtLeast( '8.1.278' ) ) diff --git a/python/ycm/tests/youcompleteme_test.py b/python/ycm/tests/youcompleteme_test.py index d86fed1e45..4f940eb954 100644 --- a/python/ycm/tests/youcompleteme_test.py +++ b/python/ycm/tests/youcompleteme_test.py @@ -30,6 +30,7 @@ from hamcrest import ( assert_that, contains_exactly, empty, equal_to, has_entries, is_in, is_not, matches_regexp ) from unittest.mock import call, MagicMock, patch +from unittest import TestCase from ycm import vimsupport from ycm.paths import _PathToPythonUsedDuringBuild @@ -48,67 +49,6 @@ MockAsyncServerResponseException ) -@YouCompleteMeInstance() -def YouCompleteMe_YcmCoreNotImported_test( ycm ): - assert_that( 'ycm_core', is_not( is_in( sys.modules ) ) ) - - -@patch( 'ycm.vimsupport.PostVimMessage' ) -def YouCompleteMe_InvalidPythonInterpreterPath_test( post_vim_message ): - with UserOptions( { - 'g:ycm_server_python_interpreter': '/invalid/python/path' } ): - try: - ycm = YouCompleteMe() - - assert_that( ycm.IsServerAlive(), equal_to( False ) ) - post_vim_message.assert_called_once_with( - "Unable to start the ycmd server. " - "Path in 'g:ycm_server_python_interpreter' option does not point " - "to a valid Python 3.6+. " - "Correct the error then restart the server with ':YcmRestartServer'." ) - - post_vim_message.reset_mock() - - SetVariableValue( 'g:ycm_server_python_interpreter', - _PathToPythonUsedDuringBuild() ) - ycm.RestartServer() - - assert_that( ycm.IsServerAlive(), equal_to( True ) ) - post_vim_message.assert_called_once_with( 'Restarting ycmd server...' ) - finally: - WaitUntilReady() - StopServer( ycm ) - - -@patch( 'ycmd.utils.PathToFirstExistingExecutable', return_value = None ) -@patch( 'ycm.paths._EndsWithPython', return_value = False ) -@patch( 'ycm.vimsupport.PostVimMessage' ) -def YouCompleteMe_NoPythonInterpreterFound_test( post_vim_message, *args ): - with UserOptions( {} ): - try: - with patch( 'ycmd.utils.ReadFile', side_effect = IOError ): - ycm = YouCompleteMe() - - assert_that( ycm.IsServerAlive(), equal_to( False ) ) - post_vim_message.assert_called_once_with( - "Unable to start the ycmd server. Cannot find Python 3.6+. " - "Set the 'g:ycm_server_python_interpreter' option to a Python " - "interpreter path. " - "Correct the error then restart the server with ':YcmRestartServer'." ) - - post_vim_message.reset_mock() - - SetVariableValue( 'g:ycm_server_python_interpreter', - _PathToPythonUsedDuringBuild() ) - ycm.RestartServer() - - assert_that( ycm.IsServerAlive(), equal_to( True ) ) - post_vim_message.assert_called_once_with( 'Restarting ycmd server...' ) - finally: - WaitUntilReady() - StopServer( ycm ) - - def RunNotifyUserIfServerCrashed( ycm, post_vim_message, test ): StopServer( ycm ) @@ -124,408 +64,6 @@ def RunNotifyUserIfServerCrashed( ycm, post_vim_message, test ): test[ 'expected_message' ] ) -@YouCompleteMeInstance() -@patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) -def YouCompleteMe_NotifyUserIfServerCrashed_UnexpectedCore_test( - post_vim_message, ycm ): - message = ( - "The ycmd server SHUT DOWN \\(restart with ':YcmRestartServer'\\). " - "Unexpected error while loading the YCM core library. Type " - "':YcmToggleLogs ycmd_\\d+_stderr_.+.log' to check the logs." ) - RunNotifyUserIfServerCrashed( ycm, post_vim_message, { - 'return_code': 3, - 'expected_message': matches_regexp( message ) - } ) - - -@YouCompleteMeInstance() -@patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) -def YouCompleteMe_NotifyUserIfServerCrashed_MissingCore_test( - post_vim_message, ycm ): - message = ( "The ycmd server SHUT DOWN (restart with ':YcmRestartServer'). " - "YCM core library not detected; you need to compile YCM before " - "using it. Follow the instructions in the documentation." ) - RunNotifyUserIfServerCrashed( ycm, post_vim_message, { - 'return_code': 4, - 'expected_message': equal_to( message ) - } ) - - -@YouCompleteMeInstance() -@patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) -def YouCompleteMe_NotifyUserIfServerCrashed_OutdatedCore_test( - post_vim_message, ycm ): - message = ( "The ycmd server SHUT DOWN (restart with ':YcmRestartServer'). " - "YCM core library too old; PLEASE RECOMPILE by running the " - "install.py script. See the documentation for more details." ) - RunNotifyUserIfServerCrashed( ycm, post_vim_message, { - 'return_code': 7, - 'expected_message': equal_to( message ) - } ) - - -@YouCompleteMeInstance() -@patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) -def YouCompleteMe_NotifyUserIfServerCrashed_UnexpectedExitCode_test( - post_vim_message, ycm ): - message = ( - "The ycmd server SHUT DOWN \\(restart with ':YcmRestartServer'\\). " - "Unexpected exit code 1. Type " - "':YcmToggleLogs ycmd_\\d+_stderr_.+.log' to check the logs." ) - RunNotifyUserIfServerCrashed( ycm, post_vim_message, { - 'return_code': 1, - 'expected_message': matches_regexp( message ) - } ) - - -@YouCompleteMeInstance( { 'g:ycm_extra_conf_vim_data': [ 'tempname()' ] } ) -def YouCompleteMe_DebugInfo_ServerRunning_test( ycm ): - dir_of_script = os.path.dirname( os.path.abspath( __file__ ) ) - buf_name = os.path.join( dir_of_script, 'testdata', 'test.cpp' ) - extra_conf = os.path.join( dir_of_script, 'testdata', '.ycm_extra_conf.py' ) - _LoadExtraConfFile( extra_conf ) - - current_buffer = VimBuffer( buf_name, filetype = 'cpp' ) - with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): - assert_that( - ycm.DebugInfo(), - matches_regexp( - 'Client logfile: .+\n' - 'Server Python interpreter: .+\n' - 'Server Python version: .+\n' - 'Server has Clang support compiled in: (False|True)\n' - 'Clang version: .+\n' - 'Extra configuration file found and loaded\n' - 'Extra configuration path: .*testdata[/\\\\]\\.ycm_extra_conf\\.py\n' - '[\\w\\W]*' - 'Server running at: .+\n' - 'Server process ID: \\d+\n' - 'Server logfiles:\n' - ' .+\n' - ' .+' ) - ) - - -@YouCompleteMeInstance() -def YouCompleteMe_DebugInfo_ServerNotRunning_test( ycm ): - StopServer( ycm ) - - current_buffer = VimBuffer( 'current_buffer' ) - with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): - assert_that( - ycm.DebugInfo(), - matches_regexp( - 'Client logfile: .+\n' - 'Server errored, no debug info from server\n' - 'Server running at: .+\n' - 'Server process ID: \\d+\n' - 'Server logfiles:\n' - ' .+\n' - ' .+' ) - ) - - -@YouCompleteMeInstance() -def YouCompleteMe_OnVimLeave_RemoveClientLogfileByDefault_test( ycm ): - client_logfile = ycm._client_logfile - assert_that( os.path.isfile( client_logfile ), - f'Logfile { client_logfile } does not exist.' ) - ycm.OnVimLeave() - assert_that( not os.path.isfile( client_logfile ), - f'Logfile { client_logfile } was not removed.' ) - - -@YouCompleteMeInstance( { 'g:ycm_keep_logfiles': 1 } ) -def YouCompleteMe_OnVimLeave_KeepClientLogfile_test( ycm ): - client_logfile = ycm._client_logfile - assert_that( os.path.isfile( client_logfile ), - f'Logfile { client_logfile } does not exist.' ) - ycm.OnVimLeave() - assert_that( os.path.isfile( client_logfile ), - f'Logfile { client_logfile } was removed.' ) - - -@YouCompleteMeInstance() -@patch( 'ycm.vimsupport.CloseBuffersForFilename', new_callable = ExtendedMock ) -@patch( 'ycm.vimsupport.OpenFilename', new_callable = ExtendedMock ) -def YouCompleteMe_ToggleLogs_WithParameters_test( open_filename, - close_buffers_for_filename, - ycm ): - logfile_buffer = VimBuffer( ycm._client_logfile ) - with MockVimBuffers( [ logfile_buffer ], [ logfile_buffer ] ): - ycm.ToggleLogs( 90, - 'botright vertical', - os.path.basename( ycm._client_logfile ), - 'nonexisting_logfile', - os.path.basename( ycm._server_stdout ) ) - - open_filename.assert_has_exact_calls( [ - call( ycm._server_stdout, { 'size': 90, - 'watch': True, - 'fix': True, - 'focus': False, - 'position': 'end', - 'mods': 'botright vertical' } ) - ] ) - close_buffers_for_filename.assert_has_exact_calls( [ - call( ycm._client_logfile ) - ] ) - - -@YouCompleteMeInstance() -# Select the second item of the list which is the ycmd stderr logfile. -@patch( 'ycm.vimsupport.SelectFromList', return_value = 1 ) -@patch( 'ycm.vimsupport.OpenFilename', new_callable = ExtendedMock ) -def YouCompleteMe_ToggleLogs_WithoutParameters_SelectLogfileNotAlreadyOpen_test( - open_filename, select_from_list, ycm ): - - current_buffer = VimBuffer( 'current_buffer' ) - with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): - ycm.ToggleLogs( 0, '' ) - - open_filename.assert_has_exact_calls( [ - call( ycm._server_stderr, { 'size': 12, - 'watch': True, - 'fix': True, - 'focus': False, - 'position': 'end', - 'mods': '' } ) - ] ) - - -@YouCompleteMeInstance() -# Select the third item of the list which is the ycmd stdout logfile. -@patch( 'ycm.vimsupport.SelectFromList', return_value = 2 ) -@patch( 'ycm.vimsupport.CloseBuffersForFilename', new_callable = ExtendedMock ) -def YouCompleteMe_ToggleLogs_WithoutParameters_SelectLogfileAlreadyOpen_test( - close_buffers_for_filename, select_from_list, ycm ): - - logfile_buffer = VimBuffer( ycm._server_stdout ) - with MockVimBuffers( [ logfile_buffer ], [ logfile_buffer ] ): - ycm.ToggleLogs( 0, '' ) - - close_buffers_for_filename.assert_has_exact_calls( [ - call( ycm._server_stdout ) - ] ) - - -@YouCompleteMeInstance() -@patch( 'ycm.vimsupport.SelectFromList', - side_effect = RuntimeError( 'Error message' ) ) -@patch( 'ycm.vimsupport.PostVimMessage' ) -def YouCompleteMe_ToggleLogs_WithoutParameters_NoSelection_test( - post_vim_message, select_from_list, ycm ): - - current_buffer = VimBuffer( 'current_buffer' ) - with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): - ycm.ToggleLogs( 0, '' ) - - assert_that( - # Argument passed to PostVimMessage. - post_vim_message.call_args[ 0 ][ 0 ], - equal_to( 'Error message' ) - ) - - -@YouCompleteMeInstance() -def YouCompleteMe_GetDefinedSubcommands_ListFromServer_test( ycm ): - current_buffer = VimBuffer( 'buffer' ) - with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): - with patch( 'ycm.client.base_request._JsonFromFuture', - return_value = [ 'SomeCommand', 'AnotherCommand' ] ): - assert_that( - ycm.GetDefinedSubcommands(), - contains_exactly( - 'SomeCommand', - 'AnotherCommand' - ) - ) - - -@YouCompleteMeInstance() -@patch( 'ycm.client.base_request._logger', autospec = True ) -@patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) -def YouCompleteMe_GetDefinedSubcommands_ErrorFromServer_test( post_vim_message, - logger, - ycm ): - current_buffer = VimBuffer( 'buffer' ) - with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): - with patch( 'ycm.client.base_request._JsonFromFuture', - side_effect = ServerError( 'Server error' ) ): - result = ycm.GetDefinedSubcommands() - - logger.exception.assert_called_with( 'Error while handling server response' ) - post_vim_message.assert_has_exact_calls( [ - call( 'Server error', truncate = False ) - ] ) - assert_that( result, empty() ) - - -@YouCompleteMeInstance() -@patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) -def YouCompleteMe_ShowDetailedDiagnostic_MessageFromServer_test( - post_vim_message, ycm ): - - current_buffer = VimBuffer( 'buffer' ) - with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): - with patch( 'ycm.client.base_request._JsonFromFuture', - return_value = { 'message': 'some_detailed_diagnostic' } ): - ycm.ShowDetailedDiagnostic(), - - post_vim_message.assert_has_exact_calls( [ - call( 'some_detailed_diagnostic', warning = False ) - ] ) - - -@YouCompleteMeInstance() -@patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) -def YouCompleteMe_ShowDetailedDiagnostic_Exception_test( - post_vim_message, ycm ): - - current_buffer = VimBuffer( 'buffer' ) - with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): - with patch( 'ycm.client.base_request._JsonFromFuture', - side_effect = RuntimeError( 'Some exception' ) ): - ycm.ShowDetailedDiagnostic(), - - post_vim_message.assert_has_exact_calls( [ - call( 'Some exception', truncate = False ) - ] ) - - -@YouCompleteMeInstance() -@patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) -def YouCompleteMe_ShowDiagnostics_FiletypeNotSupported_test( post_vim_message, - ycm ): - - current_buffer = VimBuffer( 'buffer', filetype = 'not_supported' ) - with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): - ycm.ShowDiagnostics() - - post_vim_message.assert_called_once_with( - 'Native filetype completion not supported for current file, ' - 'cannot force recompilation.', warning = False ) - - -@YouCompleteMeInstance() -@patch( 'ycm.youcompleteme.YouCompleteMe.FiletypeCompleterExistsForFiletype', - return_value = True ) -@patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) -@patch( 'ycm.vimsupport.SetLocationListForWindow', new_callable = ExtendedMock ) -def YouCompleteMe_ShowDiagnostics_NoDiagnosticsDetected_test( - set_location_list_for_window, - post_vim_message, - filetype_completer_exists, - ycm ): - - current_buffer = VimBuffer( 'buffer', filetype = 'cpp' ) - with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): - with patch( 'ycm.client.event_notification.EventNotification.Response', - return_value = {} ): - ycm.ShowDiagnostics() - - post_vim_message.assert_has_exact_calls( [ - call( 'Forcing compilation, this will block Vim until done.', - warning = False ), - call( 'Diagnostics refreshed', warning = False ), - call( 'No warnings or errors detected.', warning = False ) - ] ) - set_location_list_for_window.assert_called_once_with( 1, [] ) - - -@YouCompleteMeInstance( { 'g:ycm_log_level': 'debug', - 'g:ycm_keep_logfiles': 1, - 'g:ycm_open_loclist_on_ycm_diags': 0 } ) -@patch( 'ycm.youcompleteme.YouCompleteMe.FiletypeCompleterExistsForFiletype', - return_value = True ) -@patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) -@patch( 'ycm.vimsupport.SetLocationListForWindow', new_callable = ExtendedMock ) -def YouCompleteMe_ShowDiagnostics_DiagnosticsFound_DoNotOpenLocationList_test( - set_location_list_for_window, - post_vim_message, - filetype_completer_exists, - ycm ): - - diagnostic = { - 'kind': 'ERROR', - 'text': 'error text', - 'location': { - 'filepath': 'buffer', - 'line_num': 19, - 'column_num': 2 - } - } - - current_buffer = VimBuffer( 'buffer', - filetype = 'cpp', - number = 3 ) - with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): - with patch( 'ycm.client.event_notification.EventNotification.Response', - return_value = [ diagnostic ] ): - ycm.ShowDiagnostics() - - post_vim_message.assert_has_exact_calls( [ - call( 'Forcing compilation, this will block Vim until done.', - warning = False ), - call( 'Diagnostics refreshed', warning = False ) - ] ) - set_location_list_for_window.assert_called_once_with( 1, [ { - 'bufnr': 3, - 'lnum': 19, - 'col': 2, - 'text': 'error text', - 'type': 'E', - 'valid': 1 - } ] ) - - -@YouCompleteMeInstance( { 'g:ycm_open_loclist_on_ycm_diags': 1 } ) -@patch( 'ycm.youcompleteme.YouCompleteMe.FiletypeCompleterExistsForFiletype', - return_value = True ) -@patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) -@patch( 'ycm.vimsupport.SetLocationListForWindow', new_callable = ExtendedMock ) -@patch( 'ycm.vimsupport.OpenLocationList', new_callable = ExtendedMock ) -def YouCompleteMe_ShowDiagnostics_DiagnosticsFound_OpenLocationList_test( - open_location_list, - set_location_list_for_window, - post_vim_message, - filetype_completer_exists, - ycm ): - - diagnostic = { - 'kind': 'ERROR', - 'text': 'error text', - 'location': { - 'filepath': 'buffer', - 'line_num': 19, - 'column_num': 2 - } - } - - current_buffer = VimBuffer( 'buffer', - filetype = 'cpp', - number = 3 ) - with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): - with patch( 'ycm.client.event_notification.EventNotification.Response', - return_value = [ diagnostic ] ): - ycm.ShowDiagnostics() - - post_vim_message.assert_has_exact_calls( [ - call( 'Forcing compilation, this will block Vim until done.', - warning = False ), - call( 'Diagnostics refreshed', warning = False ) - ] ) - set_location_list_for_window.assert_called_once_with( 1, [ { - 'bufnr': 3, - 'lnum': 19, - 'col': 2, - 'text': 'error text', - 'type': 'E', - 'valid': 1 - } ] ) - open_location_list.assert_called_once_with( focus = True ) - - def YouCompleteMe_UpdateDiagnosticInterface( ycm, post_vim_message, *args ): contents = """int main() { @@ -674,291 +212,540 @@ def YouCompleteMe_UpdateDiagnosticInterface( ycm, post_vim_message, *args ): ) -@YouCompleteMeInstance( { 'g:ycm_echo_current_diagnostic': 1, - 'g:ycm_enable_diagnostic_signs': 1, - 'g:ycm_enable_diagnostic_highlighting': 1 } ) -@patch( 'ycm.youcompleteme.YouCompleteMe.FiletypeCompleterExistsForFiletype', - return_value = True ) -@patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) -@patch( 'ycm.client.event_notification.EventNotification.Done', - return_value = True ) -def YouCompleteMe_UpdateDiagnosticInterface_OldVim_test( - request_done, post_vim_message, filetype_completer_exists, ycm ): - YouCompleteMe_UpdateDiagnosticInterface( ycm, post_vim_message ) - - -@YouCompleteMeInstance( { 'g:ycm_echo_current_diagnostic': 1, - 'g:ycm_enable_diagnostic_signs': 1, - 'g:ycm_enable_diagnostic_highlighting': 1 } ) -@patch( 'ycm.youcompleteme.YouCompleteMe.FiletypeCompleterExistsForFiletype', - return_value = True ) -@patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) -@patch( 'ycm.tests.test_utils.VIM_VERSION', Version( 8, 1, 614 ) ) -@patch( 'ycm.client.event_notification.EventNotification.Done', - return_value = True ) -def YouCompleteMe_UpdateDiagnosticInterface_NewVim_test( - request_done, post_vim_message, filetype_completer_exists, ycm ): - YouCompleteMe_UpdateDiagnosticInterface( ycm, post_vim_message ) - - -@YouCompleteMeInstance( { 'g:ycm_enable_diagnostic_highlighting': 1 } ) -def YouCompleteMe_UpdateMatches_ClearDiagnosticMatchesInNewBuffer_test( ycm ): - current_buffer = VimBuffer( 'buffer', - filetype = 'c', - number = 5 ) +class YouCompleteMeTest( TestCase ): + @YouCompleteMeInstance() + def test_YouCompleteMe_YcmCoreNotImported( self, ycm ): + assert_that( 'ycm_core', is_not( is_in( sys.modules ) ) ) - test_utils.VIM_MATCHES_FOR_WINDOW[ 1 ] = [ - VimMatch( 'YcmWarningSection', '\\%3l\\%5c\\_.\\{-}\\%3l\\%7c' ), - VimMatch( 'YcmWarningSection', '\\%3l\\%3c\\_.\\{-}\\%3l\\%9c' ), - VimMatch( 'YcmErrorSection', '\\%3l\\%8c' ) - ] - with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): - ycm.UpdateMatches() + @patch( 'ycm.vimsupport.PostVimMessage' ) + def test_YouCompleteMe_InvalidPythonInterpreterPath( self, post_vim_message ): + with UserOptions( { + 'g:ycm_server_python_interpreter': '/invalid/python/path' } ): + try: + ycm = YouCompleteMe() - assert_that( test_utils.VIM_MATCHES_FOR_WINDOW, - has_entries( { 1: empty() } ) ) + assert_that( ycm.IsServerAlive(), equal_to( False ) ) + post_vim_message.assert_called_once_with( + "Unable to start the ycmd server. " + "Path in 'g:ycm_server_python_interpreter' option does not point " + "to a valid Python 3.6+. " + "Correct the error then restart the server with " + "':YcmRestartServer'." ) + + post_vim_message.reset_mock() + + SetVariableValue( 'g:ycm_server_python_interpreter', + _PathToPythonUsedDuringBuild() ) + ycm.RestartServer() + + assert_that( ycm.IsServerAlive(), equal_to( True ) ) + post_vim_message.assert_called_once_with( 'Restarting ycmd server...' ) + finally: + WaitUntilReady() + StopServer( ycm ) + + + @patch( 'ycmd.utils.PathToFirstExistingExecutable', return_value = None ) + @patch( 'ycm.paths._EndsWithPython', return_value = False ) + @patch( 'ycm.vimsupport.PostVimMessage' ) + def test_YouCompleteMe_NoPythonInterpreterFound( + self, post_vim_message, *args ): + with UserOptions( {} ): + try: + with patch( 'ycmd.utils.ReadFile', side_effect = IOError ): + ycm = YouCompleteMe() + + assert_that( ycm.IsServerAlive(), equal_to( False ) ) + post_vim_message.assert_called_once_with( + "Unable to start the ycmd server. Cannot find Python 3.6+. " + "Set the 'g:ycm_server_python_interpreter' option to a Python " + "interpreter path. " + "Correct the error then restart the server with " + "':YcmRestartServer'." ) + + post_vim_message.reset_mock() + + SetVariableValue( 'g:ycm_server_python_interpreter', + _PathToPythonUsedDuringBuild() ) + ycm.RestartServer() + + assert_that( ycm.IsServerAlive(), equal_to( True ) ) + post_vim_message.assert_called_once_with( 'Restarting ycmd server...' ) + finally: + WaitUntilReady() + StopServer( ycm ) + + + @YouCompleteMeInstance() + @patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) + def test_YouCompleteMe_NotifyUserIfServerCrashed_UnexpectedCore( + self, ycm, post_vim_message ): + message = ( + "The ycmd server SHUT DOWN \\(restart with ':YcmRestartServer'\\). " + "Unexpected error while loading the YCM core library. Type " + "':YcmToggleLogs ycmd_\\d+_stderr_.+.log' to check the logs." ) + RunNotifyUserIfServerCrashed( ycm, post_vim_message, { + 'return_code': 3, + 'expected_message': matches_regexp( message ) + } ) -@YouCompleteMeInstance( { 'g:ycm_echo_current_diagnostic': 1, - 'g:ycm_always_populate_location_list': 1, - 'g:ycm_show_diagnostics_ui': 0, - 'g:ycm_enable_diagnostic_highlighting': 1 } ) -@patch( 'ycm.youcompleteme.YouCompleteMe.FiletypeCompleterExistsForFiletype', - return_value = True ) -@patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) -def YouCompleteMe_AsyncDiagnosticUpdate_UserDisabled_test( - post_vim_message, - filetype_completer_exists, - ycm ): - diagnostics = [ - { - 'kind': 'ERROR', - 'text': 'error text in current buffer', - 'location': { - 'filepath': '/current', - 'line_num': 1, - 'column_num': 1 - }, - 'location_extent': { - 'start': { - 'filepath': '/current', - 'line_num': 1, - 'column_num': 1, - }, - 'end': { - 'filepath': '/current', - 'line_num': 1, - 'column_num': 1, - } - }, - 'ranges': [] - }, - ] - current_buffer = VimBuffer( '/current', - filetype = 'ycmtest', - contents = [ 'current' ] * 10, - number = 1 ) - buffers = [ current_buffer ] - windows = [ current_buffer ] - - # Register each buffer internally with YCM - for current in buffers: - with MockVimBuffers( buffers, [ current ] ): - ycm.OnFileReadyToParse() - with patch( 'ycm.vimsupport.SetLocationListForWindow', - new_callable = ExtendedMock ) as set_location_list_for_window: - with MockVimBuffers( buffers, windows ): - ycm.UpdateWithNewDiagnosticsForFile( '/current', diagnostics ) - - post_vim_message.assert_has_exact_calls( [] ) - set_location_list_for_window.assert_has_exact_calls( [] ) - - assert_that( - test_utils.VIM_MATCHES_FOR_WINDOW, - empty() - ) - - -@YouCompleteMeInstance( { 'g:ycm_echo_current_diagnostic': 1, - 'g:ycm_always_populate_location_list': 1, - 'g:ycm_enable_diagnostic_highlighting': 1 } ) -@patch( 'ycm.youcompleteme.YouCompleteMe.FiletypeCompleterExistsForFiletype', - return_value = True ) -@patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) -def YouCompleteMe_AsyncDiagnosticUpdate_SingleFile_test( - post_vim_message, - filetype_completer_exists, - ycm ): + @YouCompleteMeInstance() + @patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) + def test_YouCompleteMe_NotifyUserIfServerCrashed_MissingCore( + self, ycm, post_vim_message ): + message = ( "The ycmd server SHUT DOWN (restart with ':YcmRestartServer'). " + "YCM core library not detected; you need to compile YCM before " + "using it. Follow the instructions in the documentation." ) + RunNotifyUserIfServerCrashed( ycm, post_vim_message, { + 'return_code': 4, + 'expected_message': equal_to( message ) + } ) - # This test simulates asynchronous diagnostic updates associated with a single - # file (e.g. Translation Unit), but where the actual errors refer to other - # open files and other non-open files. This is not strictly invalid, nor is it - # completely normal, but it is supported and does work. - # Contrast with the next test which sends the diagnostics filewise, which is - # what the language server protocol will do. + @YouCompleteMeInstance() + @patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) + def test_YouCompleteMe_NotifyUserIfServerCrashed_OutdatedCore( + self, ycm, post_vim_message ): + message = ( "The ycmd server SHUT DOWN (restart with ':YcmRestartServer'). " + "YCM core library too old; PLEASE RECOMPILE by running the " + "install.py script. See the documentation for more details." ) + RunNotifyUserIfServerCrashed( ycm, post_vim_message, { + 'return_code': 7, + 'expected_message': equal_to( message ) + } ) - diagnostics = [ - { - 'kind': 'ERROR', - 'text': 'error text in current buffer', - 'location': { - 'filepath': '/current', - 'line_num': 1, - 'column_num': 1 - }, - 'location_extent': { - 'start': { - 'filepath': '/current', - 'line_num': 1, - 'column_num': 1, - }, - 'end': { - 'filepath': '/current', - 'line_num': 1, - 'column_num': 1, - } - }, - 'ranges': [] - }, - { - 'kind': 'ERROR', - 'text': 'error text in hidden buffer', - 'location': { - 'filepath': '/has_diags', - 'line_num': 4, - 'column_num': 2 - }, - 'location_extent': { - 'start': { - 'filepath': '/has_diags', - 'line_num': 4, - 'column_num': 2, - }, - 'end': { - 'filepath': '/has_diags', - 'line_num': 4, - 'column_num': 2, - } - }, - 'ranges': [] - }, - { - 'kind': 'ERROR', - 'text': 'error text in buffer not open in Vim', - 'location': { - 'filepath': '/not_open', - 'line_num': 8, - 'column_num': 4 - }, - 'location_extent': { - 'start': { - 'filepath': '/not_open', - 'line_num': 8, - 'column_num': 4, - }, - 'end': { - 'filepath': '/not_open', - 'line_num': 8, - 'column_num': 4, - } - }, - 'ranges': [] - } - ] - current_buffer = VimBuffer( '/current', - filetype = 'ycmtest', - contents = [ 'current' ] * 10, - number = 1 ) - no_diags_buffer = VimBuffer( '/no_diags', - filetype = 'ycmtest', - contents = [ 'nodiags' ] * 10, - number = 2 ) - hidden_buffer = VimBuffer( '/has_diags', - filetype = 'ycmtest', - contents = [ 'hasdiags' ] * 10, - number = 3 ) - - buffers = [ current_buffer, no_diags_buffer, hidden_buffer ] - windows = [ current_buffer, no_diags_buffer ] - - # Register each buffer internally with YCM - for current in buffers: - with MockVimBuffers( buffers, [ current ] ): - ycm.OnFileReadyToParse() + @YouCompleteMeInstance() + @patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) + def test_YouCompleteMe_NotifyUserIfServerCrashed_UnexpectedExitCode( + self, ycm, post_vim_message ): + message = ( + "The ycmd server SHUT DOWN \\(restart with ':YcmRestartServer'\\). " + "Unexpected exit code 1. Type " + "':YcmToggleLogs ycmd_\\d+_stderr_.+.log' to check the logs." ) + RunNotifyUserIfServerCrashed( ycm, post_vim_message, { + 'return_code': 1, + 'expected_message': matches_regexp( message ) + } ) - with patch( 'ycm.vimsupport.SetLocationListForWindow', - new_callable = ExtendedMock ) as set_location_list_for_window: - with MockVimBuffers( buffers, windows ): - ycm.UpdateWithNewDiagnosticsForFile( '/current', diagnostics ) - # We update the diagnostic on the current cursor position - post_vim_message.assert_has_exact_calls( [ - call( "error text in current buffer", truncate = True, warning = False ), - ] ) + @YouCompleteMeInstance( { 'g:ycm_extra_conf_vim_data': [ 'tempname()' ] } ) + def test_YouCompleteMe_DebugInfo_ServerRunning( self, ycm ): + dir_of_script = os.path.dirname( os.path.abspath( __file__ ) ) + buf_name = os.path.join( dir_of_script, 'testdata', 'test.cpp' ) + extra_conf = os.path.join( dir_of_script, 'testdata', '.ycm_extra_conf.py' ) + _LoadExtraConfFile( extra_conf ) - # Ensure we included all the diags though - set_location_list_for_window.assert_has_exact_calls( [ - call( 1, [ - { - 'lnum': 1, - 'col': 1, - 'bufnr': 1, - 'valid': 1, - 'type': 'E', - 'text': 'error text in current buffer', - }, - { - 'lnum': 4, - 'col': 2, - 'bufnr': 3, - 'valid': 1, - 'type': 'E', - 'text': 'error text in hidden buffer', - }, - { - 'lnum': 8, - 'col': 4, - 'bufnr': -1, # sic: Our mocked bufnr function actually returns -1, - # even though YCM is passing "create if needed". - # FIXME? we shouldn't do that, and we should pass - # filename instead - 'valid': 1, - 'type': 'E', - 'text': 'error text in buffer not open in Vim' - } - ] ) - ] ) + current_buffer = VimBuffer( buf_name, filetype = 'cpp' ) + with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): + assert_that( + ycm.DebugInfo(), + matches_regexp( + 'Client logfile: .+\n' + 'Server Python interpreter: .+\n' + 'Server Python version: .+\n' + 'Server has Clang support compiled in: (False|True)\n' + 'Clang version: .+\n' + 'Extra configuration file found and loaded\n' + 'Extra configuration path: .*testdata[/\\\\]\\.ycm_extra_conf\\.py\n' + '[\\w\\W]*' + 'Server running at: .+\n' + 'Server process ID: \\d+\n' + 'Server logfiles:\n' + ' .+\n' + ' .+' ) + ) - assert_that( - test_utils.VIM_MATCHES_FOR_WINDOW, - has_entries( { - 1: contains_exactly( - VimMatch( 'YcmErrorSection', '\\%1l\\%1c\\_.\\{-}\\%1l\\%1c' ) + + @YouCompleteMeInstance() + def test_YouCompleteMe_DebugInfo_ServerNotRunning( self, ycm ): + StopServer( ycm ) + + current_buffer = VimBuffer( 'current_buffer' ) + with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): + assert_that( + ycm.DebugInfo(), + matches_regexp( + 'Client logfile: .+\n' + 'Server errored, no debug info from server\n' + 'Server running at: .+\n' + 'Server process ID: \\d+\n' + 'Server logfiles:\n' + ' .+\n' + ' .+' ) ) - } ) - ) -@YouCompleteMeInstance( { 'g:ycm_echo_current_diagnostic': 1, - 'g:ycm_always_populate_location_list': 1, - 'g:ycm_enable_diagnostic_highlighting': 1 } ) -@patch( 'ycm.youcompleteme.YouCompleteMe.FiletypeCompleterExistsForFiletype', - return_value = True ) -@patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) -def YouCompleteMe_AsyncDiagnosticUpdate_PerFile_test( + @YouCompleteMeInstance() + def test_YouCompleteMe_OnVimLeave_RemoveClientLogfileByDefault( self, ycm ): + client_logfile = ycm._client_logfile + assert_that( os.path.isfile( client_logfile ), + f'Logfile { client_logfile } does not exist.' ) + ycm.OnVimLeave() + assert_that( not os.path.isfile( client_logfile ), + f'Logfile { client_logfile } was not removed.' ) + + + @YouCompleteMeInstance( { 'g:ycm_keep_logfiles': 1 } ) + def test_YouCompleteMe_OnVimLeave_KeepClientLogfile( self, ycm ): + client_logfile = ycm._client_logfile + assert_that( os.path.isfile( client_logfile ), + f'Logfile { client_logfile } does not exist.' ) + ycm.OnVimLeave() + assert_that( os.path.isfile( client_logfile ), + f'Logfile { client_logfile } was removed.' ) + + + @YouCompleteMeInstance() + @patch( 'ycm.vimsupport.CloseBuffersForFilename', + new_callable = ExtendedMock ) + @patch( 'ycm.vimsupport.OpenFilename', new_callable = ExtendedMock ) + def test_YouCompleteMe_ToggleLogs_WithParameters( + self, ycm, open_filename, close_buffers_for_filename ): + logfile_buffer = VimBuffer( ycm._client_logfile ) + with MockVimBuffers( [ logfile_buffer ], [ logfile_buffer ] ): + ycm.ToggleLogs( 90, + 'botright vertical', + os.path.basename( ycm._client_logfile ), + 'nonexisting_logfile', + os.path.basename( ycm._server_stdout ) ) + + open_filename.assert_has_exact_calls( [ + call( ycm._server_stdout, { 'size': 90, + 'watch': True, + 'fix': True, + 'focus': False, + 'position': 'end', + 'mods': 'botright vertical' } ) + ] ) + close_buffers_for_filename.assert_has_exact_calls( [ + call( ycm._client_logfile ) + ] ) + + + @YouCompleteMeInstance() + # Select the second item of the list which is the ycmd stderr logfile. + @patch( 'ycm.vimsupport.SelectFromList', return_value = 1 ) + @patch( 'ycm.vimsupport.OpenFilename', new_callable = ExtendedMock ) + def test_YouCompleteMe_ToggleLogs_WithoutParameters_SelectLogfileNotAlreadyOpen( # noqa + self, ycm, open_filename, *args ): + + current_buffer = VimBuffer( 'current_buffer' ) + with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): + ycm.ToggleLogs( 0, '' ) + + open_filename.assert_has_exact_calls( [ + call( ycm._server_stderr, { 'size': 12, + 'watch': True, + 'fix': True, + 'focus': False, + 'position': 'end', + 'mods': '' } ) + ] ) + + + @YouCompleteMeInstance() + # Select the third item of the list which is the ycmd stdout logfile. + @patch( 'ycm.vimsupport.SelectFromList', return_value = 2 ) + @patch( 'ycm.vimsupport.CloseBuffersForFilename', + new_callable = ExtendedMock ) + def test_YouCompleteMe_ToggleLogs_WithoutParameters_SelectLogfileAlreadyOpen( + self, ycm, close_buffers_for_filename, *args ): + + logfile_buffer = VimBuffer( ycm._server_stdout ) + with MockVimBuffers( [ logfile_buffer ], [ logfile_buffer ] ): + ycm.ToggleLogs( 0, '' ) + + close_buffers_for_filename.assert_has_exact_calls( [ + call( ycm._server_stdout ) + ] ) + + + @YouCompleteMeInstance() + @patch( 'ycm.vimsupport.SelectFromList', + side_effect = RuntimeError( 'Error message' ) ) + @patch( 'ycm.vimsupport.PostVimMessage' ) + def test_YouCompleteMe_ToggleLogs_WithoutParameters_NoSelection( + self, ycm, post_vim_message, *args ): + + current_buffer = VimBuffer( 'current_buffer' ) + with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): + ycm.ToggleLogs( 0, '' ) + + assert_that( + # Argument passed to PostVimMessage. + post_vim_message.call_args[ 0 ][ 0 ], + equal_to( 'Error message' ) + ) + + + @YouCompleteMeInstance() + def test_YouCompleteMe_GetDefinedSubcommands_ListFromServer( self, ycm ): + current_buffer = VimBuffer( 'buffer' ) + with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): + with patch( 'ycm.client.base_request._JsonFromFuture', + return_value = [ 'SomeCommand', 'AnotherCommand' ] ): + assert_that( + ycm.GetDefinedSubcommands(), + contains_exactly( + 'SomeCommand', + 'AnotherCommand' + ) + ) + + + @YouCompleteMeInstance() + @patch( 'ycm.client.base_request._logger', autospec = True ) + @patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) + def test_YouCompleteMe_GetDefinedSubcommands_ErrorFromServer( + self, ycm, post_vim_message, logger ): + current_buffer = VimBuffer( 'buffer' ) + with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): + with patch( 'ycm.client.base_request._JsonFromFuture', + side_effect = ServerError( 'Server error' ) ): + result = ycm.GetDefinedSubcommands() + + logger.exception.assert_called_with( + 'Error while handling server response' ) + post_vim_message.assert_has_exact_calls( [ + call( 'Server error', truncate = False ) + ] ) + assert_that( result, empty() ) + + + @YouCompleteMeInstance() + @patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) + def test_YouCompleteMe_ShowDetailedDiagnostic_MessageFromServer( + self, ycm, post_vim_message ): + + current_buffer = VimBuffer( 'buffer' ) + with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): + with patch( 'ycm.client.base_request._JsonFromFuture', + return_value = { 'message': 'some_detailed_diagnostic' } ): + ycm.ShowDetailedDiagnostic(), + + post_vim_message.assert_has_exact_calls( [ + call( 'some_detailed_diagnostic', warning = False ) + ] ) + + + @YouCompleteMeInstance() + @patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) + def test_YouCompleteMe_ShowDetailedDiagnostic_Exception( + self, ycm, post_vim_message ): + + current_buffer = VimBuffer( 'buffer' ) + with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): + with patch( 'ycm.client.base_request._JsonFromFuture', + side_effect = RuntimeError( 'Some exception' ) ): + ycm.ShowDetailedDiagnostic(), + + post_vim_message.assert_has_exact_calls( [ + call( 'Some exception', truncate = False ) + ] ) + + + @YouCompleteMeInstance() + @patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) + def test_YouCompleteMe_ShowDiagnostics_FiletypeNotSupported( + self, ycm, post_vim_message ): + + current_buffer = VimBuffer( 'buffer', filetype = 'not_supported' ) + with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): + ycm.ShowDiagnostics() + + post_vim_message.assert_called_once_with( + 'Native filetype completion not supported for current file, ' + 'cannot force recompilation.', warning = False ) + + + @YouCompleteMeInstance() + @patch( 'ycm.youcompleteme.YouCompleteMe.FiletypeCompleterExistsForFiletype', + return_value = True ) + @patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) + @patch( 'ycm.vimsupport.SetLocationListForWindow', + new_callable = ExtendedMock ) + def test_YouCompleteMe_ShowDiagnostics_NoDiagnosticsDetected( + self, + ycm, + set_location_list_for_window, + post_vim_message, + *args ): + + current_buffer = VimBuffer( 'buffer', filetype = 'cpp' ) + with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): + with patch( 'ycm.client.event_notification.EventNotification.Response', + return_value = {} ): + ycm.ShowDiagnostics() + + post_vim_message.assert_has_exact_calls( [ + call( 'Forcing compilation, this will block Vim until done.', + warning = False ), + call( 'Diagnostics refreshed', warning = False ), + call( 'No warnings or errors detected.', warning = False ) + ] ) + set_location_list_for_window.assert_called_once_with( 1, [] ) + + + @YouCompleteMeInstance( { 'g:ycm_log_level': 'debug', + 'g:ycm_keep_logfiles': 1, + 'g:ycm_open_loclist_on_ycm_diags': 0 } ) + @patch( 'ycm.youcompleteme.YouCompleteMe.FiletypeCompleterExistsForFiletype', + return_value = True ) + @patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) + @patch( 'ycm.vimsupport.SetLocationListForWindow', + new_callable = ExtendedMock ) + def test_YouCompleteMe_ShowDiagnostics_DiagnosticsFound_DoNotOpenLocationList( + self, + ycm, + set_location_list_for_window, post_vim_message, - filetype_completer_exists, - ycm ): + *args ): + diagnostic = { + 'kind': 'ERROR', + 'text': 'error text', + 'location': { + 'filepath': 'buffer', + 'line_num': 19, + 'column_num': 2 + } + } - # This test simulates asynchronous diagnostic updates which are delivered per - # file, including files which are open and files which are not. + current_buffer = VimBuffer( 'buffer', + filetype = 'cpp', + number = 3 ) + with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): + with patch( 'ycm.client.event_notification.EventNotification.Response', + return_value = [ diagnostic ] ): + ycm.ShowDiagnostics() + + post_vim_message.assert_has_exact_calls( [ + call( 'Forcing compilation, this will block Vim until done.', + warning = False ), + call( 'Diagnostics refreshed', warning = False ) + ] ) + set_location_list_for_window.assert_called_once_with( 1, [ { + 'bufnr': 3, + 'lnum': 19, + 'col': 2, + 'text': 'error text', + 'type': 'E', + 'valid': 1 + } ] ) + + + @YouCompleteMeInstance( { 'g:ycm_open_loclist_on_ycm_diags': 1 } ) + @patch( 'ycm.youcompleteme.YouCompleteMe.FiletypeCompleterExistsForFiletype', + return_value = True ) + @patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) + @patch( 'ycm.vimsupport.SetLocationListForWindow', + new_callable = ExtendedMock ) + @patch( 'ycm.vimsupport.OpenLocationList', new_callable = ExtendedMock ) + def test_YouCompleteMe_ShowDiagnostics_DiagnosticsFound_OpenLocationList( + self, + ycm, + open_location_list, + set_location_list_for_window, + post_vim_message, + *args ): - # Ordered to ensure that the calls to update are deterministic - diagnostics_per_file = [ - ( '/current', [ { + diagnostic = { + 'kind': 'ERROR', + 'text': 'error text', + 'location': { + 'filepath': 'buffer', + 'line_num': 19, + 'column_num': 2 + } + } + + current_buffer = VimBuffer( 'buffer', + filetype = 'cpp', + number = 3 ) + with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): + with patch( 'ycm.client.event_notification.EventNotification.Response', + return_value = [ diagnostic ] ): + ycm.ShowDiagnostics() + + post_vim_message.assert_has_exact_calls( [ + call( 'Forcing compilation, this will block Vim until done.', + warning = False ), + call( 'Diagnostics refreshed', warning = False ) + ] ) + set_location_list_for_window.assert_called_once_with( 1, [ { + 'bufnr': 3, + 'lnum': 19, + 'col': 2, + 'text': 'error text', + 'type': 'E', + 'valid': 1 + } ] ) + open_location_list.assert_called_once_with( focus = True ) + + + @YouCompleteMeInstance( { 'g:ycm_echo_current_diagnostic': 1, + 'g:ycm_enable_diagnostic_signs': 1, + 'g:ycm_enable_diagnostic_highlighting': 1 } ) + @patch( 'ycm.youcompleteme.YouCompleteMe.FiletypeCompleterExistsForFiletype', + return_value = True ) + @patch( 'ycm.client.event_notification.EventNotification.Done', + return_value = True ) + @patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) + def test_YouCompleteMe_UpdateDiagnosticInterface_OldVim( + self, ycm, post_vim_message, *args ): + YouCompleteMe_UpdateDiagnosticInterface( ycm, post_vim_message ) + + + @YouCompleteMeInstance( { 'g:ycm_echo_current_diagnostic': 1, + 'g:ycm_enable_diagnostic_signs': 1, + 'g:ycm_enable_diagnostic_highlighting': 1 } ) + @patch( 'ycm.youcompleteme.YouCompleteMe.FiletypeCompleterExistsForFiletype', + return_value = True ) + @patch( 'ycm.tests.test_utils.VIM_VERSION', Version( 8, 1, 614 ) ) + @patch( 'ycm.client.event_notification.EventNotification.Done', + return_value = True ) + @patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) + def test_YouCompleteMe_UpdateDiagnosticInterface_NewVim( + self, ycm, post_vim_message, *args ): + YouCompleteMe_UpdateDiagnosticInterface( ycm, post_vim_message ) + + + @YouCompleteMeInstance( { 'g:ycm_enable_diagnostic_highlighting': 1 } ) + def test_YouCompleteMe_UpdateMatches_ClearDiagnosticMatchesInNewBuffer( + self, ycm ): + current_buffer = VimBuffer( 'buffer', + filetype = 'c', + number = 5 ) + + test_utils.VIM_MATCHES_FOR_WINDOW[ 1 ] = [ + VimMatch( 'YcmWarningSection', '\\%3l\\%5c\\_.\\{-}\\%3l\\%7c' ), + VimMatch( 'YcmWarningSection', '\\%3l\\%3c\\_.\\{-}\\%3l\\%9c' ), + VimMatch( 'YcmErrorSection', '\\%3l\\%8c' ) + ] + + with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): + ycm.UpdateMatches() + + assert_that( test_utils.VIM_MATCHES_FOR_WINDOW, + has_entries( { 1: empty() } ) ) + + + @YouCompleteMeInstance( { 'g:ycm_echo_current_diagnostic': 1, + 'g:ycm_always_populate_location_list': 1, + 'g:ycm_show_diagnostics_ui': 0, + 'g:ycm_enable_diagnostic_highlighting': 1 } ) + @patch( 'ycm.youcompleteme.YouCompleteMe.FiletypeCompleterExistsForFiletype', + return_value = True ) + @patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) + def test_YouCompleteMe_AsyncDiagnosticUpdate_UserDisabled( + self, + ycm, + post_vim_message, + *args ): + diagnostics = [ + { 'kind': 'ERROR', 'text': 'error text in current buffer', 'location': { @@ -978,53 +765,100 @@ def YouCompleteMe_AsyncDiagnosticUpdate_PerFile_test( 'column_num': 1, } }, - 'ranges': [], - } ] ), - ( '/separate_window', [ { + 'ranges': [] + }, + ] + current_buffer = VimBuffer( '/current', + filetype = 'ycmtest', + contents = [ 'current' ] * 10, + number = 1 ) + buffers = [ current_buffer ] + windows = [ current_buffer ] + + # Register each buffer internally with YCM + for current in buffers: + with MockVimBuffers( buffers, [ current ] ): + ycm.OnFileReadyToParse() + with patch( 'ycm.vimsupport.SetLocationListForWindow', + new_callable = ExtendedMock ) as set_location_list_for_window: + with MockVimBuffers( buffers, windows ): + ycm.UpdateWithNewDiagnosticsForFile( '/current', diagnostics ) + + post_vim_message.assert_has_exact_calls( [] ) + set_location_list_for_window.assert_has_exact_calls( [] ) + + assert_that( + test_utils.VIM_MATCHES_FOR_WINDOW, + empty() + ) + + + @YouCompleteMeInstance( { 'g:ycm_echo_current_diagnostic': 1, + 'g:ycm_always_populate_location_list': 1, + 'g:ycm_enable_diagnostic_highlighting': 1 } ) + @patch( 'ycm.youcompleteme.YouCompleteMe.FiletypeCompleterExistsForFiletype', + return_value = True ) + @patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) + def test_YouCompleteMe_AsyncDiagnosticUpdate_SingleFile( + self, + ycm, + post_vim_message, + *args ): + + # This test simulates asynchronous diagnostic updates associated with a + # single file (e.g. Translation Unit), but where the actual errors refer to + # other open files and other non-open files. This is not strictly invalid, + # nor is it completely normal, but it is supported and does work. + + # Contrast with the next test which sends the diagnostics filewise, which + # is what the language server protocol will do. + + diagnostics = [ + { 'kind': 'ERROR', - 'text': 'error text in a buffer open in a separate window', + 'text': 'error text in current buffer', 'location': { - 'filepath': '/separate_window', - 'line_num': 3, - 'column_num': 3 + 'filepath': '/current', + 'line_num': 1, + 'column_num': 1 }, 'location_extent': { 'start': { - 'filepath': '/separate_window', - 'line_num': 3, - 'column_num': 3, + 'filepath': '/current', + 'line_num': 1, + 'column_num': 1, }, 'end': { - 'filepath': '/separate_window', - 'line_num': 3, - 'column_num': 3, + 'filepath': '/current', + 'line_num': 1, + 'column_num': 1, } }, 'ranges': [] - } ] ), - ( '/hidden', [ { + }, + { 'kind': 'ERROR', 'text': 'error text in hidden buffer', 'location': { - 'filepath': '/hidden', + 'filepath': '/has_diags', 'line_num': 4, 'column_num': 2 }, 'location_extent': { 'start': { - 'filepath': '/hidden', + 'filepath': '/has_diags', 'line_num': 4, 'column_num': 2, }, 'end': { - 'filepath': '/hidden', + 'filepath': '/has_diags', 'line_num': 4, 'column_num': 2, } }, 'ranges': [] - } ] ), - ( '/not_open', [ { + }, + { 'kind': 'ERROR', 'text': 'error text in buffer not open in Vim', 'location': { @@ -1045,252 +879,450 @@ def YouCompleteMe_AsyncDiagnosticUpdate_PerFile_test( } }, 'ranges': [] - } ] ) - ] - - current_buffer = VimBuffer( '/current', - filetype = 'ycmtest', - contents = [ 'current' ] * 10, - number = 1 ) - no_diags_buffer = VimBuffer( '/no_diags', - filetype = 'ycmtest', - contents = [ 'no_diags' ] * 10, - number = 2 ) - separate_window = VimBuffer( '/separate_window', + } + ] + + current_buffer = VimBuffer( '/current', + filetype = 'ycmtest', + contents = [ 'current' ] * 10, + number = 1 ) + no_diags_buffer = VimBuffer( '/no_diags', + filetype = 'ycmtest', + contents = [ 'nodiags' ] * 10, + number = 2 ) + hidden_buffer = VimBuffer( '/has_diags', filetype = 'ycmtest', - contents = [ 'separate_window' ] * 10, + contents = [ 'hasdiags' ] * 10, number = 3 ) - hidden_buffer = VimBuffer( '/hidden', - filetype = 'ycmtest', - contents = [ 'hidden' ] * 10, - number = 4 ) - buffers = [ - current_buffer, - no_diags_buffer, - separate_window, - hidden_buffer - ] - windows = [ - current_buffer, - no_diags_buffer, - separate_window - ] - - # Register each buffer internally with YCM - for current in buffers: - with MockVimBuffers( buffers, [ current ] ): - ycm.OnFileReadyToParse() - with patch( 'ycm.vimsupport.SetLocationListForWindow', - new_callable = ExtendedMock ) as set_location_list_for_window: - with MockVimBuffers( buffers, windows ): - for filename, diagnostics in diagnostics_per_file: - ycm.UpdateWithNewDiagnosticsForFile( filename, diagnostics ) + buffers = [ current_buffer, no_diags_buffer, hidden_buffer ] + windows = [ current_buffer, no_diags_buffer ] - # We update the diagnostic on the current cursor position - post_vim_message.assert_has_exact_calls( [ - call( "error text in current buffer", truncate = True, warning = False ), - ] ) + # Register each buffer internally with YCM + for current in buffers: + with MockVimBuffers( buffers, [ current ] ): + ycm.OnFileReadyToParse() - # Ensure we included all the diags though - set_location_list_for_window.assert_has_exact_calls( [ - call( 1, [ - { - 'lnum': 1, - 'col': 1, - 'bufnr': 1, - 'valid': 1, - 'type': 'E', - 'text': 'error text in current buffer', - }, - ] ), + with patch( 'ycm.vimsupport.SetLocationListForWindow', + new_callable = ExtendedMock ) as set_location_list_for_window: + with MockVimBuffers( buffers, windows ): + ycm.UpdateWithNewDiagnosticsForFile( '/current', diagnostics ) - call( 3, [ - { - 'lnum': 3, - 'col': 3, - 'bufnr': 3, - 'valid': 1, - 'type': 'E', - 'text': 'error text in a buffer open in a separate window', - }, + # We update the diagnostic on the current cursor position + post_vim_message.assert_has_exact_calls( [ + call( "error text in current buffer", truncate = True, warning = False ), ] ) - ] ) - - # FIXME: diagnostic matches in windows other than the current one are not - # updated. - assert_that( - test_utils.VIM_MATCHES_FOR_WINDOW, - has_entries( { - 1: contains_exactly( - VimMatch( 'YcmErrorSection', '\\%1l\\%1c\\_.\\{-}\\%1l\\%1c' ) - ) - } ) - ) + # Ensure we included all the diags though + set_location_list_for_window.assert_has_exact_calls( [ + call( 1, [ + { + 'lnum': 1, + 'col': 1, + 'bufnr': 1, + 'valid': 1, + 'type': 'E', + 'text': 'error text in current buffer', + }, + { + 'lnum': 4, + 'col': 2, + 'bufnr': 3, + 'valid': 1, + 'type': 'E', + 'text': 'error text in hidden buffer', + }, + { + 'lnum': 8, + 'col': 4, + 'bufnr': -1, # sic: Our mocked bufnr function actually returns -1, + # even though YCM is passing "create if needed". + # FIXME? we shouldn't do that, and we should pass + # filename instead + 'valid': 1, + 'type': 'E', + 'text': 'error text in buffer not open in Vim' + } + ] ) + ] ) -@YouCompleteMeInstance() -def YouCompleteMe_OnPeriodicTick_ServerNotRunning_test( ycm ): - with patch.object( ycm, 'IsServerAlive', return_value = False ): - assert_that( ycm.OnPeriodicTick(), equal_to( False ) ) + assert_that( + test_utils.VIM_MATCHES_FOR_WINDOW, + has_entries( { + 1: contains_exactly( + VimMatch( 'YcmErrorSection', '\\%1l\\%1c\\_.\\{-}\\%1l\\%1c' ) + ) + } ) + ) -@YouCompleteMeInstance() -def YouCompleteMe_OnPeriodicTick_ServerNotReady_test( ycm ): - with patch.object( ycm, 'IsServerAlive', return_value = True ): - with patch.object( ycm, 'IsServerReady', return_value = False ): - assert_that( ycm.OnPeriodicTick(), equal_to( True ) ) + @YouCompleteMeInstance( { 'g:ycm_echo_current_diagnostic': 1, + 'g:ycm_always_populate_location_list': 1, + 'g:ycm_enable_diagnostic_highlighting': 1 } ) + @patch( 'ycm.youcompleteme.YouCompleteMe.FiletypeCompleterExistsForFiletype', + return_value = True ) + @patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) + def test_YouCompleteMe_AsyncDiagnosticUpdate_PerFile( + self, + ycm, + post_vim_message, + *args ): + + # This test simulates asynchronous diagnostic updates which are delivered + # per file, including files which are open and files which are not. + + # Ordered to ensure that the calls to update are deterministic + diagnostics_per_file = [ + ( '/current', [ { + 'kind': 'ERROR', + 'text': 'error text in current buffer', + 'location': { + 'filepath': '/current', + 'line_num': 1, + 'column_num': 1 + }, + 'location_extent': { + 'start': { + 'filepath': '/current', + 'line_num': 1, + 'column_num': 1, + }, + 'end': { + 'filepath': '/current', + 'line_num': 1, + 'column_num': 1, + } + }, + 'ranges': [], + } ] ), + ( '/separate_window', [ { + 'kind': 'ERROR', + 'text': 'error text in a buffer open in a separate window', + 'location': { + 'filepath': '/separate_window', + 'line_num': 3, + 'column_num': 3 + }, + 'location_extent': { + 'start': { + 'filepath': '/separate_window', + 'line_num': 3, + 'column_num': 3, + }, + 'end': { + 'filepath': '/separate_window', + 'line_num': 3, + 'column_num': 3, + } + }, + 'ranges': [] + } ] ), + ( '/hidden', [ { + 'kind': 'ERROR', + 'text': 'error text in hidden buffer', + 'location': { + 'filepath': '/hidden', + 'line_num': 4, + 'column_num': 2 + }, + 'location_extent': { + 'start': { + 'filepath': '/hidden', + 'line_num': 4, + 'column_num': 2, + }, + 'end': { + 'filepath': '/hidden', + 'line_num': 4, + 'column_num': 2, + } + }, + 'ranges': [] + } ] ), + ( '/not_open', [ { + 'kind': 'ERROR', + 'text': 'error text in buffer not open in Vim', + 'location': { + 'filepath': '/not_open', + 'line_num': 8, + 'column_num': 4 + }, + 'location_extent': { + 'start': { + 'filepath': '/not_open', + 'line_num': 8, + 'column_num': 4, + }, + 'end': { + 'filepath': '/not_open', + 'line_num': 8, + 'column_num': 4, + } + }, + 'ranges': [] + } ] ) + ] + + current_buffer = VimBuffer( '/current', + filetype = 'ycmtest', + contents = [ 'current' ] * 10, + number = 1 ) + no_diags_buffer = VimBuffer( '/no_diags', + filetype = 'ycmtest', + contents = [ 'no_diags' ] * 10, + number = 2 ) + separate_window = VimBuffer( '/separate_window', + filetype = 'ycmtest', + contents = [ 'separate_window' ] * 10, + number = 3 ) + hidden_buffer = VimBuffer( '/hidden', + filetype = 'ycmtest', + contents = [ 'hidden' ] * 10, + number = 4 ) + buffers = [ + current_buffer, + no_diags_buffer, + separate_window, + hidden_buffer + ] + windows = [ + current_buffer, + no_diags_buffer, + separate_window + ] + + # Register each buffer internally with YCM + for current in buffers: + with MockVimBuffers( buffers, [ current ] ): + ycm.OnFileReadyToParse() + + with patch( 'ycm.vimsupport.SetLocationListForWindow', + new_callable = ExtendedMock ) as set_location_list_for_window: + with MockVimBuffers( buffers, windows ): + for filename, diagnostics in diagnostics_per_file: + ycm.UpdateWithNewDiagnosticsForFile( filename, diagnostics ) + + # We update the diagnostic on the current cursor position + post_vim_message.assert_has_exact_calls( [ + call( "error text in current buffer", truncate = True, warning = False ), + ] ) + + # Ensure we included all the diags though + set_location_list_for_window.assert_has_exact_calls( [ + call( 1, [ + { + 'lnum': 1, + 'col': 1, + 'bufnr': 1, + 'valid': 1, + 'type': 'E', + 'text': 'error text in current buffer', + }, + ] ), + + call( 3, [ + { + 'lnum': 3, + 'col': 3, + 'bufnr': 3, + 'valid': 1, + 'type': 'E', + 'text': 'error text in a buffer open in a separate window', + }, + ] ) + ] ) + # FIXME: diagnostic matches in windows other than the current one are not + # updated. + assert_that( + test_utils.VIM_MATCHES_FOR_WINDOW, + has_entries( { + 1: contains_exactly( + VimMatch( 'YcmErrorSection', '\\%1l\\%1c\\_.\\{-}\\%1l\\%1c' ) + ) + } ) + ) -@YouCompleteMeInstance() -@patch( 'ycm.youcompleteme.YouCompleteMe.FiletypeCompleterExistsForFiletype', - return_value = True ) -@patch( 'ycm.client.base_request._ValidateResponseObject', return_value = True ) -@patch( 'ycm.client.base_request.BaseRequest.PostDataToHandlerAsync' ) -def YouCompleteMe_OnPeriodicTick_DontRetry_test( - post_data_to_handler_async, - validate_response_object, - filetype_completer_exists, - ycm ): - - current_buffer = VimBuffer( '/current', - filetype = 'ycmtest', - number = 1 ) - - # Create the request and make the first poll; we expect no response - with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 1 ) ): - assert_that( ycm.OnPeriodicTick(), equal_to( True ) ) - post_data_to_handler_async.assert_called() - - assert ycm._message_poll_requests[ 'ycmtest' ] is not None - post_data_to_handler_async.reset_mock() - - # OK that sent the request, now poll to check if it is complete (say it is - # not) - with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 1 ) ) as v: - mock_response = MockAsyncServerResponseInProgress() - with patch.dict( ycm._message_poll_requests, {} ): - ycm._message_poll_requests[ 'ycmtest' ] = MessagesPoll( v.current.buffer ) - ycm._message_poll_requests[ 'ycmtest' ]._response_future = mock_response - mock_future = ycm._message_poll_requests[ 'ycmtest' ]._response_future - poll_again = ycm.OnPeriodicTick() - mock_future.done.assert_called() - mock_future.result.assert_not_called() - assert_that( poll_again, equal_to( True ) ) - - # Poll again, but return a response (telling us to stop polling) - with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 1 ) ) as v: - mock_response = MockAsyncServerResponseDone( False ) - with patch.dict( ycm._message_poll_requests, {} ): - ycm._message_poll_requests[ 'ycmtest' ] = MessagesPoll( v.current.buffer ) - ycm._message_poll_requests[ 'ycmtest' ]._response_future = mock_response - mock_future = ycm._message_poll_requests[ 'ycmtest' ]._response_future - poll_again = ycm.OnPeriodicTick() - mock_future.done.assert_called() - mock_future.result.assert_called() - post_data_to_handler_async.assert_not_called() - # We reset and don't poll anymore - assert_that( ycm._message_poll_requests[ 'ycmtest' ] is None ) - assert_that( poll_again, equal_to( False ) ) - - - -@YouCompleteMeInstance() -@patch( 'ycm.youcompleteme.YouCompleteMe.FiletypeCompleterExistsForFiletype', - return_value = True ) -@patch( 'ycm.client.base_request._ValidateResponseObject', return_value = True ) -@patch( 'ycm.client.base_request.BaseRequest.PostDataToHandlerAsync' ) -def YouCompleteMe_OnPeriodicTick_Exception_test( post_data_to_handler_async, - validate_response_object, - filetype_completer_exists, - ycm ): - - current_buffer = VimBuffer( '/current', - filetype = 'ycmtest', - number = 1 ) - - # Create the request and make the first poll; we expect no response - with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 1 ) ): - assert_that( ycm.OnPeriodicTick(), equal_to( True ) ) - post_data_to_handler_async.assert_called() - - post_data_to_handler_async.reset_mock() - - # Poll again, but return an exception response - with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 1 ) ) as v: - mock_response = MockAsyncServerResponseException( RuntimeError( 'test' ) ) - with patch.dict( ycm._message_poll_requests, {} ): - ycm._message_poll_requests[ 'ycmtest' ] = MessagesPoll( v.current.buffer ) - ycm._message_poll_requests[ 'ycmtest' ]._response_future = mock_response - mock_future = ycm._message_poll_requests[ 'ycmtest' ]._response_future + + @YouCompleteMeInstance() + def test_YouCompleteMe_OnPeriodicTick_ServerNotRunning( self, ycm ): + with patch.object( ycm, 'IsServerAlive', return_value = False ): assert_that( ycm.OnPeriodicTick(), equal_to( False ) ) - mock_future.done.assert_called() - mock_future.result.assert_called() - post_data_to_handler_async.assert_not_called() - # We reset and don't poll anymore - assert_that( ycm._message_poll_requests[ 'ycmtest' ] is None ) - - -@YouCompleteMeInstance() -@patch( 'ycm.youcompleteme.YouCompleteMe.FiletypeCompleterExistsForFiletype', - return_value = True ) -@patch( 'ycm.client.base_request._ValidateResponseObject', return_value = True ) -@patch( 'ycm.client.base_request.BaseRequest.PostDataToHandlerAsync' ) -@patch( 'ycm.client.messages_request._HandlePollResponse' ) -def YouCompleteMe_OnPeriodicTick_ValidResponse_test( handle_poll_response, - post_data_to_handler_async, - validate_response_object, - filetype_completer_exists, - ycm ): - - current_buffer = VimBuffer( '/current', - filetype = 'ycmtest', - number = 1 ) - - # Create the request and make the first poll; we expect no response - with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 1 ) ): - assert_that( ycm.OnPeriodicTick(), equal_to( True ) ) - post_data_to_handler_async.assert_called() - - post_data_to_handler_async.reset_mock() - - # Poll again, and return a _proper_ response (finally!). - # Note, _HandlePollResponse is tested independently (for simplicity) - with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 1 ) ) as v: - mock_response = MockAsyncServerResponseDone( [] ) - with patch.dict( ycm._message_poll_requests, {} ): - ycm._message_poll_requests[ 'ycmtest' ] = MessagesPoll( v.current.buffer ) - ycm._message_poll_requests[ 'ycmtest' ]._response_future = mock_response - mock_future = ycm._message_poll_requests[ 'ycmtest' ]._response_future + + + @YouCompleteMeInstance() + def test_YouCompleteMe_OnPeriodicTick_ServerNotReady( self, ycm ): + with patch.object( ycm, 'IsServerAlive', return_value = True ): + with patch.object( ycm, 'IsServerReady', return_value = False ): + assert_that( ycm.OnPeriodicTick(), equal_to( True ) ) + + + @YouCompleteMeInstance() + @patch( 'ycm.youcompleteme.YouCompleteMe.FiletypeCompleterExistsForFiletype', + return_value = True ) + @patch( 'ycm.client.base_request._ValidateResponseObject', + return_value = True ) + @patch( 'ycm.client.base_request.BaseRequest.PostDataToHandlerAsync' ) + def test_YouCompleteMe_OnPeriodicTick_DontRetry( + self, + ycm, + post_data_to_handler_async, + *args ): + + current_buffer = VimBuffer( '/current', + filetype = 'ycmtest', + number = 1 ) + + # Create the request and make the first poll; we expect no response + with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 1 ) ): + assert_that( ycm.OnPeriodicTick(), equal_to( True ) ) + post_data_to_handler_async.assert_called() + + assert ycm._message_poll_requests[ 'ycmtest' ] is not None + post_data_to_handler_async.reset_mock() + + # OK that sent the request, now poll to check if it is complete (say it is + # not) + with MockVimBuffers( [ current_buffer ], + [ current_buffer ], + ( 1, 1 ) ) as v: + mock_response = MockAsyncServerResponseInProgress() + with patch.dict( ycm._message_poll_requests, {} ): + ycm._message_poll_requests[ 'ycmtest' ] = MessagesPoll( + v.current.buffer ) + ycm._message_poll_requests[ 'ycmtest' ]._response_future = mock_response + mock_future = ycm._message_poll_requests[ 'ycmtest' ]._response_future + poll_again = ycm.OnPeriodicTick() + mock_future.done.assert_called() + mock_future.result.assert_not_called() + assert_that( poll_again, equal_to( True ) ) + + # Poll again, but return a response (telling us to stop polling) + with MockVimBuffers( [ current_buffer ], + [ current_buffer ], + ( 1, 1 ) ) as v: + mock_response = MockAsyncServerResponseDone( False ) + with patch.dict( ycm._message_poll_requests, {} ): + ycm._message_poll_requests[ 'ycmtest' ] = MessagesPoll( + v.current.buffer ) + ycm._message_poll_requests[ 'ycmtest' ]._response_future = mock_response + mock_future = ycm._message_poll_requests[ 'ycmtest' ]._response_future + poll_again = ycm.OnPeriodicTick() + mock_future.done.assert_called() + mock_future.result.assert_called() + post_data_to_handler_async.assert_not_called() + # We reset and don't poll anymore + assert_that( ycm._message_poll_requests[ 'ycmtest' ] is None ) + assert_that( poll_again, equal_to( False ) ) + + + + @YouCompleteMeInstance() + @patch( 'ycm.youcompleteme.YouCompleteMe.FiletypeCompleterExistsForFiletype', + return_value = True ) + @patch( 'ycm.client.base_request._ValidateResponseObject', + return_value = True ) + @patch( 'ycm.client.base_request.BaseRequest.PostDataToHandlerAsync' ) + def test_YouCompleteMe_OnPeriodicTick_Exception( + self, + ycm, + post_data_to_handler_async, + *args ): + + current_buffer = VimBuffer( '/current', + filetype = 'ycmtest', + number = 1 ) + + # Create the request and make the first poll; we expect no response + with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 1 ) ): + assert_that( ycm.OnPeriodicTick(), equal_to( True ) ) + post_data_to_handler_async.assert_called() + + post_data_to_handler_async.reset_mock() + + # Poll again, but return an exception response + with MockVimBuffers( [ current_buffer ], + [ current_buffer ], + ( 1, 1 ) ) as v: + mock_response = MockAsyncServerResponseException( + RuntimeError( 'test' ) ) + with patch.dict( ycm._message_poll_requests, {} ): + ycm._message_poll_requests[ 'ycmtest' ] = MessagesPoll( + v.current.buffer ) + ycm._message_poll_requests[ 'ycmtest' ]._response_future = mock_response + mock_future = ycm._message_poll_requests[ 'ycmtest' ]._response_future + assert_that( ycm.OnPeriodicTick(), equal_to( False ) ) + mock_future.done.assert_called() + mock_future.result.assert_called() + post_data_to_handler_async.assert_not_called() + # We reset and don't poll anymore + assert_that( ycm._message_poll_requests[ 'ycmtest' ] is None ) + + + @YouCompleteMeInstance() + @patch( 'ycm.youcompleteme.YouCompleteMe.FiletypeCompleterExistsForFiletype', + return_value = True ) + @patch( 'ycm.client.base_request._ValidateResponseObject', + return_value = True ) + @patch( 'ycm.client.base_request.BaseRequest.PostDataToHandlerAsync' ) + @patch( 'ycm.client.messages_request._HandlePollResponse' ) + def test_YouCompleteMe_OnPeriodicTick_ValidResponse( + self, ycm, handle_poll_response, post_data_to_handler_async, *args ): + + current_buffer = VimBuffer( '/current', + filetype = 'ycmtest', + number = 1 ) + + # Create the request and make the first poll; we expect no response + with MockVimBuffers( [ current_buffer ], + [ current_buffer ], + ( 1, 1 ) ): assert_that( ycm.OnPeriodicTick(), equal_to( True ) ) - handle_poll_response.assert_called() - mock_future.done.assert_called() - mock_future.result.assert_called() - post_data_to_handler_async.assert_called() # Poll again! - assert_that( ycm._message_poll_requests[ 'ycmtest' ] is not None ) - - -@YouCompleteMeInstance() -@patch( 'ycm.client.completion_request.CompletionRequest.OnCompleteDone' ) -def YouCompleteMe_OnCompleteDone_CompletionRequest_test( on_complete_done, - ycm ): - current_buffer = VimBuffer( 'current_buffer' ) - with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 1 ) ): - ycm.SendCompletionRequest() - ycm.OnCompleteDone() - on_complete_done.assert_called() - - -@YouCompleteMeInstance() -@patch( 'ycm.client.completion_request.CompletionRequest.OnCompleteDone' ) -def YouCompleteMe_OnCompleteDone_NoCompletionRequest_test( on_complete_done, - ycm ): - ycm.OnCompleteDone() - on_complete_done.assert_not_called() - - -@YouCompleteMeInstance() -def YouCompleteMe_ShouldResendFileParseRequest_NoParseRequest_test( ycm ): - current_buffer = VimBuffer( 'current_buffer' ) - with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): - assert_that( ycm.ShouldResendFileParseRequest(), equal_to( False ) ) + post_data_to_handler_async.assert_called() + + post_data_to_handler_async.reset_mock() + + # Poll again, and return a _proper_ response (finally!). + # Note, _HandlePollResponse is tested independently (for simplicity) + with MockVimBuffers( [ current_buffer ], + [ current_buffer ], + ( 1, 1 ) ) as v: + mock_response = MockAsyncServerResponseDone( [] ) + with patch.dict( ycm._message_poll_requests, {} ): + ycm._message_poll_requests[ 'ycmtest' ] = MessagesPoll( + v.current.buffer ) + ycm._message_poll_requests[ 'ycmtest' ]._response_future = mock_response + mock_future = ycm._message_poll_requests[ 'ycmtest' ]._response_future + assert_that( ycm.OnPeriodicTick(), equal_to( True ) ) + handle_poll_response.assert_called() + mock_future.done.assert_called() + mock_future.result.assert_called() + post_data_to_handler_async.assert_called() # Poll again! + assert_that( ycm._message_poll_requests[ 'ycmtest' ] is not None ) + + + @YouCompleteMeInstance() + @patch( 'ycm.client.completion_request.CompletionRequest.OnCompleteDone' ) + def test_YouCompleteMe_OnCompleteDone_CompletionRequest( + self, ycm, on_complete_done ): + current_buffer = VimBuffer( 'current_buffer' ) + with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 1 ) ): + ycm.SendCompletionRequest() + ycm.OnCompleteDone() + on_complete_done.assert_called() + + + @YouCompleteMeInstance() + @patch( 'ycm.client.completion_request.CompletionRequest.OnCompleteDone' ) + def test_YouCompleteMe_OnCompleteDone_NoCompletionRequest( + self, ycm, on_complete_done ): + ycm.OnCompleteDone() + on_complete_done.assert_not_called() + + + @YouCompleteMeInstance() + def test_YouCompleteMe_ShouldResendFileParseRequest_NoParseRequest( + self, ycm ): + current_buffer = VimBuffer( 'current_buffer' ) + with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): + assert_that( ycm.ShouldResendFileParseRequest(), equal_to( False ) ) diff --git a/run_tests.py b/run_tests.py index b99b90ba26..628ccce6be 100755 --- a/run_tests.py +++ b/run_tests.py @@ -20,13 +20,7 @@ # # Pip knows how to install this correctly so that it doesn't matter where in # sys.path the path is. -python_path = [ p.join( DIR_OF_THIRD_PARTY, 'pythonfutures' ), - p.join( DIR_OF_THIRD_PARTY, 'requests-futures' ), - p.join( DIR_OF_THIRD_PARTY, 'requests_deps', 'chardet' ), - p.join( DIR_OF_THIRD_PARTY, 'requests_deps', 'certifi' ), - p.join( DIR_OF_THIRD_PARTY, 'requests_deps', 'idna' ), - p.join( DIR_OF_THIRD_PARTY, 'requests_deps', 'requests' ), - p.join( DIR_OF_THIRD_PARTY, 'requests_deps', 'urllib3', 'src' ), +python_path = [ p.join( DIR_OF_THIS_SCRIPT, 'python' ), p.join( DIR_OF_THIRD_PARTY, 'ycmd' ) ] if os.environ.get( 'PYTHONPATH' ): python_path.append( os.environ[ 'PYTHONPATH' ] ) @@ -56,12 +50,12 @@ def ParseArguments(): help = 'Dump the PYTHONPATH required to run tests ' 'manually, then exit.' ) - parsed_args, pytests_args = parser.parse_known_args() + parsed_args, unittest_args = parser.parse_known_args() if 'COVERAGE' in os.environ: parsed_args.coverage = ( os.environ[ 'COVERAGE' ] == 'true' ) - return parsed_args, pytests_args + return parsed_args, unittest_args def BuildYcmdLibs( args ): @@ -72,22 +66,31 @@ def BuildYcmdLibs( args ): ] ) -def PytestTests( parsed_args, extra_pytests_args ): - pytests_args = [] +def UnittestTests( parsed_args, extra_unittest_args ): + unittest_args = [ '-cb' ] + prefer_regular = any( p.isfile( arg ) for arg in extra_unittest_args ) + if not prefer_regular: + unittest_args += [ '-p', '*_test.py' ] - if parsed_args.coverage: - pytests_args += [ '--cov=ycm' ] + if extra_unittest_args: + unittest_args.extend( extra_unittest_args ) + if not ( prefer_regular and extra_unittest_args ): + unittest_args.append( '-s' ) + unittest_args.append( p.join( DIR_OF_THIS_SCRIPT, 'python', 'ycm', 'tests' ) ) - if extra_pytests_args: - pytests_args.extend( extra_pytests_args ) + if parsed_args.coverage: + executable = [ sys.executable, '-m', 'coverage', 'run' ] else: - pytests_args.append( p.join( DIR_OF_THIS_SCRIPT, 'python' ) ) + executable = [ sys.executable ] - subprocess.check_call( [ sys.executable, '-m', 'pytest' ] + pytests_args ) + unittest = [ '-m', 'unittest' ] + if not prefer_regular: + unittest.append( 'discover' ) + subprocess.check_call( executable + unittest + unittest_args ) def Main(): - ( parsed_args, pytests_args ) = ParseArguments() + ( parsed_args, unittest_args ) = ParseArguments() if parsed_args.dump_path: print( os.environ[ 'PYTHONPATH' ] ) sys.exit() @@ -96,7 +99,7 @@ def Main(): RunFlake8() BuildYcmdLibs( parsed_args ) - PytestTests( parsed_args, pytests_args ) + UnittestTests( parsed_args, unittest_args ) if __name__ == "__main__":