1# Copyright (C) 2011-2020 ycmd contributors 2# 3# This file is part of ycmd. 4# 5# ycmd is free software: you can redistribute it and/or modify 6# it under the terms of the GNU General Public License as published by 7# the Free Software Foundation, either version 3 of the License, or 8# (at your option) any later version. 9# 10# ycmd is distributed in the hope that it will be useful, 11# but WITHOUT ANY WARRANTY; without even the implied warranty of 12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13# GNU General Public License for more details. 14# 15# You should have received a copy of the GNU General Public License 16# along with ycmd. If not, see <http://www.gnu.org/licenses/>. 17 18from unittest.mock import patch 19import psutil 20 21from hamcrest import ( assert_that, 22 contains_exactly, 23 empty, 24 has_entries, 25 has_entry, 26 has_item ) 27 28from ycmd import handlers, utils 29from ycmd.tests.clangd import ( IsolatedYcmd, PathToTestFile, 30 RunAfterInitialized ) 31from ycmd.tests.test_utils import ( BuildRequest, 32 CompleterProjectDirectoryMatcher, 33 MockProcessTerminationTimingOut, 34 StopCompleterServer, 35 WaitUntilCompleterServerReady ) 36 37 38def GetDebugInfo( app ): 39 request_data = BuildRequest( filetype = 'cpp' ) 40 return app.post_json( '/debug_info', request_data ).json 41 42 43def GetPid( app ): 44 return GetDebugInfo( app )[ 'completer' ][ 'servers' ][ 0 ][ 'pid' ] 45 46 47def StartClangd( app, filepath = PathToTestFile( 'basic.cpp' ) ): 48 request_data = BuildRequest( filepath = filepath, 49 filetype = 'cpp' ) 50 test = { 'request': request_data } 51 RunAfterInitialized( app, test ) 52 53 54def CheckStopped( app ): 55 assert_that( 56 GetDebugInfo( app ), 57 has_entry( 'completer', has_entries( { 58 'name': 'C-family', 59 'servers': contains_exactly( has_entries( { 60 'name': 'Clangd', 61 'pid': None, 62 'is_running': False 63 } ) ), 64 'items': empty() 65 } ) ) 66 ) 67 68 69@IsolatedYcmd() 70def ServerManagement_StopServer_Clean_test( app ): 71 StartClangd( app ) 72 StopCompleterServer( app, 'cpp', '' ) 73 CheckStopped( app ) 74 75 76@IsolatedYcmd() 77@patch( 'os.remove', side_effect = OSError ) 78@patch( 'ycmd.utils.WaitUntilProcessIsTerminated', 79 MockProcessTerminationTimingOut ) 80def ServerManagement_StopServer_Unclean_test( rm, app ): 81 StartClangd( app ) 82 StopCompleterServer( app, 'cpp', '' ) 83 CheckStopped( app ) 84 85 86@IsolatedYcmd() 87def ServerManagement_StopServer_Twice_test( app ): 88 StartClangd( app ) 89 StopCompleterServer( app, 'cpp', '' ) 90 CheckStopped( app ) 91 StopCompleterServer( app, 'cpp', '' ) 92 CheckStopped( app ) 93 94 95@IsolatedYcmd() 96def ServerManagement_StopServer_Killed_test( app ): 97 StartClangd( app ) 98 process = psutil.Process( GetPid( app ) ) 99 process.terminate() 100 process.wait( timeout = 5 ) 101 StopCompleterServer( app, 'cpp', '' ) 102 CheckStopped( app ) 103 104 105@IsolatedYcmd() 106def ServerManagement_ServerDiesWhileShuttingDown_test( app ): 107 StartClangd( app ) 108 process = psutil.Process( GetPid( app ) ) 109 completer = handlers._server_state.GetFiletypeCompleter( [ 'cpp' ] ) 110 111 # We issue a shutdown but make sure it never reaches server by mocking 112 # WriteData in Connection. Then we kill the server and check shutdown still 113 # succeeds. 114 with patch.object( completer.GetConnection(), 'WriteData' ): 115 stop_server_task = utils.StartThread( StopCompleterServer, app, 'cpp', '' ) 116 process.terminate() 117 stop_server_task.join() 118 119 CheckStopped( app ) 120 121 122@IsolatedYcmd() 123def ServerManagement_ConnectionRaisesWhileShuttingDown_test( app ): 124 StartClangd( app ) 125 process = psutil.Process( GetPid( app ) ) 126 completer = handlers._server_state.GetFiletypeCompleter( [ 'cpp' ] ) 127 128 # We issue a shutdown but make sure it never reaches server by mocking 129 # WriteData in Connection. Then we kill the server and check shutdown still 130 # succeeds. 131 with patch.object( completer.GetConnection(), 'GetResponse', 132 side_effect = RuntimeError ): 133 StopCompleterServer( app, 'cpp', '' ) 134 135 CheckStopped( app ) 136 if process.is_running(): 137 process.terminate() 138 raise AssertionError( 'Termination failed' ) 139 140 141@IsolatedYcmd() 142def ServerManagement_RestartServer_test( app ): 143 StartClangd( app, PathToTestFile( 'basic.cpp' ) ) 144 145 assert_that( 146 GetDebugInfo( app ), 147 CompleterProjectDirectoryMatcher( PathToTestFile() ) ) 148 149 app.post_json( 150 '/run_completer_command', 151 BuildRequest( 152 filepath = PathToTestFile( 'test-include', 'main.cpp' ), 153 filetype = 'cpp', 154 command_arguments = [ 'RestartServer' ], 155 ), 156 ) 157 158 WaitUntilCompleterServerReady( app, 'cpp' ) 159 160 assert_that( 161 GetDebugInfo( app ), 162 has_entry( 'completer', has_entries( { 163 'name': 'C-family', 164 'servers': contains_exactly( has_entries( { 165 'name': 'Clangd', 166 'is_running': True, 167 'extras': has_item( has_entries( { 168 'key': 'Project Directory', 169 'value': PathToTestFile( 'test-include' ), 170 } ) ) 171 } ) ) 172 } ) ) 173 ) 174 175 176def Dummy_test(): 177 # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 178 assert True 179