From c5b87a73ee7e987ae6df1e593a0493cd4d4c61de Mon Sep 17 00:00:00 2001 From: Boris Staletic Date: Thu, 8 Aug 2024 23:05:53 +0200 Subject: [PATCH 1/7] Switch C# completer to LSP Currently missing pieces: - GetDoc just does not exist in LSP mode. - CodeActions are ill-formed and hit a million asserts in ycmd. - Currently only works with user_option roslyn_binary_path. - GetProjectRootFiles returns an empty list, so users need to cd to their desired project root. --- ycmd/completers/cs/cs_completer.py | 940 +----------------------- ycmd/completers/cs/solutiondetection.py | 125 ---- 2 files changed, 26 insertions(+), 1039 deletions(-) delete mode 100644 ycmd/completers/cs/solutiondetection.py diff --git a/ycmd/completers/cs/cs_completer.py b/ycmd/completers/cs/cs_completer.py index 18f52691a4..4bbd648cb1 100644 --- a/ycmd/completers/cs/cs_completer.py +++ b/ycmd/completers/cs/cs_completer.py @@ -15,33 +15,16 @@ # You should have received a copy of the GNU General Public License # along with ycmd. If not, see . -from collections import defaultdict import os -import errno -import json -import time -import urllib.request -import urllib.error -import threading -from urllib.parse import urljoin +import logging -from ycmd.completers.completer import Completer -from ycmd.completers.completer_utils import GetFileLines -from ycmd.completers.cs import solutiondetection -from ycmd.utils import ( ByteOffsetToCodepointOffset, - CodepointOffsetToByteOffset, - FindExecutable, - FindExecutableWithFallback, - LOGGER, - ToBytes ) +from ycmd.completers.language_server import language_server_completer from ycmd import responses +from ycmd.utils import ( FindExecutable, + FindExecutableWithFallback, + LOGGER ) from ycmd import utils -SERVER_NOT_FOUND_MSG = ( 'OmniSharp server binary not found at {0}. ' + - 'Did you compile it? You can do so by running ' + - '"./install.py --cs-completer".' ) -INVALID_FILE_MESSAGE = 'File is invalid.' -NO_DIAGNOSTIC_MESSAGE = 'No diagnostic for current line!' PATH_TO_ROSLYN_OMNISHARP = os.path.join( os.path.abspath( os.path.dirname( __file__ ) ), '..', '..', '..', 'third_party', 'omnisharp-roslyn' @@ -53,7 +36,6 @@ PATH_TO_ROSLYN_OMNISHARP, 'omnisharp', 'OmniSharp.exe' ) ) ): PATH_TO_OMNISHARP_ROSLYN_BINARY = ( os.path.join( PATH_TO_ROSLYN_OMNISHARP, 'omnisharp', 'OmniSharp.exe' ) ) -LOGFILE_FORMAT = 'omnisharp_{port}_{sln}_{std}_' def ShouldEnableCsCompleter( user_options ): @@ -67,915 +49,45 @@ def ShouldEnableCsCompleter( user_options ): roslyn = user_roslyn_path else: roslyn = PATH_TO_OMNISHARP_ROSLYN_BINARY - mono = FindExecutableWithFallback( user_options[ 'mono_binary_path' ], - FindExecutable( 'mono' ) ) - if roslyn and ( mono or utils.OnWindows() ): + if roslyn: return True LOGGER.info( 'No mono executable at %s', mono ) return False -class CsharpCompleter( Completer ): - """ - A Completer that uses the Omnisharp server as completion engine. - """ - +class CsharpCompleter( language_server_completer.LanguageServerCompleter ): def __init__( self, user_options ): super().__init__( user_options ) - self._solution_for_file = {} - self._completer_per_solution = {} - self._diagnostic_store = None - self._solution_state_lock = threading.Lock() - self.SetSignatureHelpTriggers( [ '(', ',' ] ) if os.path.isfile( user_options[ 'roslyn_binary_path' ] ): self._roslyn_path = user_options[ 'roslyn_binary_path' ] else: self._roslyn_path = PATH_TO_OMNISHARP_ROSLYN_BINARY - self._mono_path = FindExecutableWithFallback( - user_options[ 'mono_binary_path' ], - FindExecutable( 'mono' ) ) - - - def Shutdown( self ): - if self.user_options[ 'auto_stop_csharp_server' ]: - for solutioncompleter in self._completer_per_solution.values(): - solutioncompleter._StopServer() - - - def SupportedFiletypes( self ): - """ Just csharp """ - return [ 'cs' ] - - - def _GetSolutionCompleter( self, request_data ): - """ Get the solution completer or create a new one if it does not already - exist. Use a lock to avoid creating the same solution completer multiple - times.""" - solution = self._GetSolutionFile( request_data[ "filepath" ] ) - - with self._solution_state_lock: - if solution not in self._completer_per_solution: - keep_logfiles = self.user_options[ 'server_keep_logfiles' ] - desired_omnisharp_port = self.user_options.get( 'csharp_server_port' ) - completer = CsharpSolutionCompleter( solution, - keep_logfiles, - desired_omnisharp_port, - self._roslyn_path, - self._mono_path ) - self._completer_per_solution[ solution ] = completer - - return self._completer_per_solution[ solution ] - - - def SignatureHelpAvailable( self ): - if not self.ServerIsHealthy(): - return responses.SignatureHelpAvailalability.PENDING - return responses.SignatureHelpAvailalability.AVAILABLE - - - def ComputeSignaturesInner( self, request_data ): - response = self._SolutionSubcommand( request_data, '_SignatureHelp' ) - - if response is None: - return {} - - signatures = response[ 'Signatures' ] - - def MakeSignature( s ): - sig_label = s[ 'Label' ] - end = 0 - parameters = [] - for arg in s[ 'Parameters' ]: - arg_label = arg[ 'Label' ] - begin = sig_label.find( arg_label, end ) - end = begin + len( arg_label ) - parameters.append( { - 'documentation': arg.get( 'Documentation', '' ), - 'label': [ CodepointOffsetToByteOffset( sig_label, begin ), - CodepointOffsetToByteOffset( sig_label, end ) ] - } ) - - return { - 'documentation': s.get( 'Documentation', '' ), - 'label': sig_label, - 'parameters': parameters - } - - return { - 'activeSignature': response[ 'ActiveSignature' ], - 'activeParameter': response[ 'ActiveParameter' ], - 'signatures': [ MakeSignature( s ) for s in signatures ] - } - - - def ResolveFixit( self, request_data ): - return self._SolutionSubcommand( request_data, '_ResolveFixIt' ) - - - def ComputeCandidatesInner( self, request_data ): - solutioncompleter = self._GetSolutionCompleter( request_data ) - return [ responses.BuildCompletionData( - completion[ 'CompletionText' ], - completion[ 'DisplayText' ], - completion[ 'Description' ], - None, - completion[ 'Kind' ] ) - for completion - in solutioncompleter._GetCompletions( request_data ) ] - - - def GetSubcommandsMap( self ): - return { - 'StopServer' : ( lambda self, request_data, args: - self._SolutionSubcommand( request_data, - method = '_StopServer', - no_request_data = True ) ), - 'RestartServer' : ( lambda self, request_data, args: - self._SolutionSubcommand( request_data, - method = '_RestartServer', - no_request_data = True ) ), - 'GoToDefinition' : ( lambda self, request_data, args: - self._SolutionSubcommand( request_data, - method = '_GoToDefinition' ) ), - 'GoToDeclaration' : ( lambda self, request_data, args: - self._SolutionSubcommand( request_data, - method = '_GoToDefinition' ) ), - 'GoTo' : ( lambda self, request_data, args: - self._SolutionSubcommand( request_data, - method = '_GoToImplementation', - fallback_to_declaration = True ) ), - 'GoToDefinitionElseDeclaration' : ( lambda self, request_data, args: - self._SolutionSubcommand( request_data, - method = '_GoToDefinition' ) ), - 'GoToReferences' : ( lambda self, request_data, args: - self._SolutionSubcommand( request_data, - method = '_GoToReferences' ) ), - 'GoToImplementation' : ( lambda self, request_data, args: - self._SolutionSubcommand( request_data, - method = '_GoToImplementation', - fallback_to_declaration = False ) ), - 'GoToImplementationElseDeclaration': ( lambda self, request_data, args: - self._SolutionSubcommand( request_data, - method = '_GoToImplementation', - fallback_to_declaration = True ) ), - 'GetType' : ( lambda self, request_data, args: - self._SolutionSubcommand( request_data, - method = '_GetType' ) ), - 'Format' : ( lambda self, request_data, args: - self._SolutionSubcommand( request_data, - method = '_Format' ) ), - 'FixIt' : ( lambda self, request_data, args: - self._SolutionSubcommand( request_data, - method = '_FixIt' ) ), - 'GetDoc' : ( lambda self, request_data, args: - self._SolutionSubcommand( request_data, - method = '_GetDoc' ) ), - 'GoToSymbol' : ( lambda self, request_data, args: - self._SolutionSubcommand( request_data, - method = '_GoToSymbol', - args = args ) ), - 'OrganizeImports' : ( lambda self, request_data, args: - self._SolutionSubcommand( request_data, - method = '_OrganizeImports' ) ), - 'RefactorRename' : ( lambda self, request_data, args: - self._SolutionSubcommand( request_data, - method = '_RefactorRename', - args = args ) ), - 'GoToDocumentOutline' : ( lambda self, request_data, args: - self._SolutionSubcommand( request_data, - method = '_GoToDocumentOutline' ) ), - } - - - def _SolutionSubcommand( self, request_data, method, - no_request_data = False, **kwargs ): - solutioncompleter = self._GetSolutionCompleter( request_data ) - if not no_request_data: - kwargs[ 'request_data' ] = request_data - return getattr( solutioncompleter, method )( **kwargs ) - - - def OnFileReadyToParse( self, request_data ): - solutioncompleter = self._GetSolutionCompleter( request_data ) - - # Only start the server associated to this solution if the option to - # automatically start one is set and no server process is already running. - if ( self.user_options[ 'auto_start_csharp_server' ] - and not solutioncompleter._ServerIsRunning() ): - solutioncompleter._StartServer() - return - - # Bail out if the server is unresponsive. We don't start or restart the - # server in this case because current one may still be warming up. - if not solutioncompleter.ServerIsHealthy(): - return - - errors = solutioncompleter.CodeCheck( request_data ) - - diagnostics = [ self._QuickFixToDiagnostic( request_data, x ) for x in - errors[ "QuickFixes" ] ] - - self._diagnostic_store = DiagnosticsToDiagStructure( diagnostics ) - - return responses.BuildDiagnosticResponse( diagnostics, - request_data[ 'filepath' ], - self.max_diagnostics_to_display ) - - - def _QuickFixToDiagnostic( self, request_data, quick_fix ): - filename = quick_fix[ "FileName" ] - # NOTE: end of diagnostic range returned by the OmniSharp server is not - # included. - location = _BuildLocation( request_data, - filename, - quick_fix[ 'Line' ], - quick_fix[ 'Column' ] ) - location_end = _BuildLocation( request_data, - filename, - quick_fix[ 'EndLine' ], - quick_fix[ 'EndColumn' ] ) - if not location_end: - location_end = location - location_extent = responses.Range( location, location_end ) - return responses.Diagnostic( [], - location, - location_extent, - quick_fix[ 'Text' ], - quick_fix[ 'LogLevel' ].upper() ) - - - def GetDetailedDiagnostic( self, request_data ): - current_line = request_data[ 'line_num' ] - current_column = request_data[ 'column_num' ] - current_file = request_data[ 'filepath' ] - - if not self._diagnostic_store: - raise ValueError( NO_DIAGNOSTIC_MESSAGE ) - - diagnostics = self._diagnostic_store[ current_file ][ current_line ] - if not diagnostics: - raise ValueError( NO_DIAGNOSTIC_MESSAGE ) - - # Prefer errors to warnings and warnings to infos. - diagnostics.sort( key = _CsDiagnosticToLspSeverity ) - - closest_diagnostic = None - distance_to_closest_diagnostic = 999 - - # FIXME: all of these calculations are currently working with byte - # offsets, which are technically incorrect. We should be working with - # codepoint offsets, as we want the nearest character-wise diagnostic - for diagnostic in diagnostics: - distance = abs( current_column - diagnostic.location_.column_number_ ) - if distance < distance_to_closest_diagnostic: - distance_to_closest_diagnostic = distance - closest_diagnostic = diagnostic - - return responses.BuildDisplayMessageResponse( - closest_diagnostic.text_ ) - - - def DebugInfo( self, request_data ): - try: - completer = self._GetSolutionCompleter( request_data ) - except RuntimeError: - omnisharp_server = responses.DebugInfoServer( - name = 'OmniSharp', - handle = None, - executable = self._roslyn_path ) - - return responses.BuildDebugInfoResponse( name = 'C#', - servers = [ omnisharp_server ] ) - - with completer._server_state_lock: - solution_item = responses.DebugInfoItem( - key = 'solution', - value = completer._solution_path ) - - omnisharp_server = responses.DebugInfoServer( - name = 'OmniSharp', - handle = completer._omnisharp_phandle, - executable = ' '.join( completer._ConstructOmnisharpCommand() ), - address = 'localhost', - port = completer._omnisharp_port, - logfiles = [ completer._filename_stdout, completer._filename_stderr ], - extras = [ solution_item ] ) - - return responses.BuildDebugInfoResponse( name = 'C#', - servers = [ omnisharp_server ] ) - - - def ServerIsHealthy( self ): - """ Check if our OmniSharp server is healthy (up and serving). """ - return self._CheckAllRunning( lambda i: i.ServerIsHealthy() ) - - - def ServerIsReady( self ): - """ Check if our OmniSharp server is ready (loaded solution file).""" - return self._CheckAllRunning( lambda i: i.ServerIsReady() ) - - - def _CheckAllRunning( self, action ): - solutioncompleters = self._completer_per_solution.values() - return all( action( completer ) for completer in solutioncompleters - if completer._ServerIsRunning() ) - - - def _GetSolutionFile( self, filepath ): - if filepath not in self._solution_for_file: - # NOTE: detection could throw an exception if an extra_conf_store needs - # to be confirmed - path_to_solutionfile = solutiondetection.FindSolutionPath( filepath ) - if not path_to_solutionfile: - raise RuntimeError( 'Autodetection of solution file failed.' ) - self._solution_for_file[ filepath ] = path_to_solutionfile - - return self._solution_for_file[ filepath ] - - -class CsharpSolutionCompleter( object ): - def __init__( self, - solution_path, - keep_logfiles, - desired_omnisharp_port, - roslyn_path, - mono_path ): - self._solution_path = solution_path - self._keep_logfiles = keep_logfiles - self._filename_stderr = None - self._filename_stdout = None - self._omnisharp_command = None - self._omnisharp_port = None - self._omnisharp_phandle = None - self._desired_omnisharp_port = desired_omnisharp_port - self._server_state_lock = threading.Lock() - self._roslyn_path = roslyn_path - self._mono_path = mono_path - - - def CodeCheck( self, request_data ): - filename = request_data[ 'filepath' ] - if not filename: - raise ValueError( INVALID_FILE_MESSAGE ) - - return self._GetResponse( '/codecheck', - self._DefaultParameters( request_data ) ) - - - def _StartServer( self ): - with self._server_state_lock: - return self._StartServerNoLock() - - - def _ConstructOmnisharpCommand( self ): - if self._omnisharp_command: - return self._omnisharp_command - - self._ChooseOmnisharpPort() - self._omnisharp_command = [ self._roslyn_path, - '-p', - str( self._omnisharp_port ), - '-s', - str( self._solution_path ) ] - - if ( not utils.OnWindows() - and self._roslyn_path.endswith( '.exe' ) ): - self._omnisharp_command.insert( 0, self._mono_path ) - - return self._omnisharp_command - - - def _StartServerNoLock( self ): - """ Start the OmniSharp server if not already running. Use a lock to avoid - starting the server multiple times for the same solution. """ - if self._ServerIsRunning(): - return - - LOGGER.info( 'Starting OmniSharp server' ) - LOGGER.info( 'Loading solution file %s', self._solution_path ) - - command = self._ConstructOmnisharpCommand() - LOGGER.info( 'Starting OmniSharp server with: %s', command ) - - solutionfile = os.path.basename( self._solution_path ) - self._filename_stdout = utils.CreateLogfile( - LOGFILE_FORMAT.format( port = self._omnisharp_port, - sln = solutionfile, - std = 'stdout' ) ) - self._filename_stderr = utils.CreateLogfile( - LOGFILE_FORMAT.format( port = self._omnisharp_port, - sln = solutionfile, - std = 'stderr' ) ) - - with utils.OpenForStdHandle( self._filename_stderr ) as fstderr: - with utils.OpenForStdHandle( self._filename_stdout ) as fstdout: - self._omnisharp_phandle = utils.SafePopen( - command, stdout = fstdout, stderr = fstderr ) - - LOGGER.info( 'Started OmniSharp server' ) - def _StopServer( self ): - with self._server_state_lock: - return self._StopServerNoLock() + def GetServerName( self ): + return 'OmniSharp-Roslyn' - def _StopServerNoLock( self ): - """ Stop the OmniSharp server using a lock. """ - if self._ServerIsRunning(): - LOGGER.info( 'Stopping OmniSharp server with PID %s', - self._omnisharp_phandle.pid ) - try: - self._TryToStopServer() - self._ForceStopServer() - utils.WaitUntilProcessIsTerminated( self._omnisharp_phandle, - timeout = 5 ) - LOGGER.info( 'OmniSharp server stopped' ) - except Exception: - LOGGER.exception( 'Error while stopping OmniSharp server' ) + def GetProjectRootFiles( self ): + return [ '*.csproj' ] - self._CleanUp() + def GetCommandLine( self ): + # TODO: User options? + cmdline = [ self._roslyn_path, '-lsp' ] + if utils.LOGGER.isEnabledFor( logging.DEBUG ): + cmdline += [ '-v' ] + return cmdline - def _TryToStopServer( self ): - for _ in range( 5 ): - try: - self._GetResponse( '/stopserver', timeout = .5 ) - except Exception: - pass - for _ in range( 10 ): - if not self._ServerIsRunning(): - return - time.sleep( .1 ) - - def _ForceStopServer( self ): - # Kill it if it's still up - phandle = self._omnisharp_phandle - if phandle is not None: - LOGGER.info( 'Killing OmniSharp server' ) - for stream in [ phandle.stderr, phandle.stdout ]: - if stream is not None: - stream.close() - try: - phandle.kill() - except OSError as e: - if e.errno == errno.ESRCH: # No such process - pass - else: - raise - - - def _CleanUp( self ): - self._omnisharp_command = None - self._omnisharp_port = None - self._omnisharp_phandle = None - if not self._keep_logfiles: - if self._filename_stdout: - utils.RemoveIfExists( self._filename_stdout ) - self._filename_stdout = None - if self._filename_stderr: - utils.RemoveIfExists( self._filename_stderr ) - self._filename_stderr = None - - - def _RestartServer( self ): - """ Restarts the OmniSharp server using a lock. """ - with self._server_state_lock: - self._StopServerNoLock() - return self._StartServerNoLock() - - - def _GetCompletions( self, request_data ): - """ Ask server for completions """ - parameters = self._DefaultParameters( request_data ) - parameters[ 'WantSnippet' ] = False - parameters[ 'WantKind' ] = True - parameters[ 'WantReturnType' ] = False - parameters[ 'WantDocumentationForEveryCompletionResult' ] = True - completions = self._GetResponse( '/autocomplete', parameters ) - return completions if completions is not None else [] - - - def _GoToDefinition( self, request_data ): - """ Jump to definition of identifier under cursor """ - definition = self._GetResponse( '/gotodefinition', - self._DefaultParameters( request_data ) ) - if definition[ 'FileName' ] is not None: - filepath = definition[ 'FileName' ] - return responses.BuildGoToResponseFromLocation( - _BuildLocation( request_data, - filepath, - definition[ 'Line' ], - definition[ 'Column' ] ) ) - else: - raise RuntimeError( 'Can\'t jump to definition' ) - - - def _GoToImplementation( self, request_data, fallback_to_declaration ): - """ Jump to implementation of identifier under cursor """ - try: - implementation = self._GetResponse( - '/findimplementations', - self._DefaultParameters( request_data ) ) - except ValueError: - implementation = { 'QuickFixes': None } - - quickfixes = implementation[ 'QuickFixes' ] - if quickfixes: - if len( quickfixes ) == 1: - impl = quickfixes[ 0 ] - return responses.BuildGoToResponseFromLocation( - _BuildLocation( - request_data, - impl[ 'FileName' ], - impl[ 'Line' ], - impl[ 'Column' ] ) ) - else: - return [ responses.BuildGoToResponseFromLocation( - _BuildLocation( request_data, - x[ 'FileName' ], - x[ 'Line' ], - x[ 'Column' ] ) ) - for x in quickfixes ] - else: - if ( fallback_to_declaration ): - return self._GoToDefinition( request_data ) - elif quickfixes is None: - raise RuntimeError( 'Can\'t jump to implementation' ) - else: - raise RuntimeError( 'No implementations found' ) - - - def _SignatureHelp( self, request_data ): - request = self._DefaultParameters( request_data ) - return self._GetResponse( '/signatureHelp', request ) - - - def _RefactorRename( self, request_data, args ): - request = self._DefaultParameters( request_data ) - if len( args ) != 1: - raise ValueError( 'Please specify a new name to rename it to.\n' - 'Usage: RefactorRename ' ) - request[ 'RenameTo' ] = args[ 0 ] - request[ 'WantsTextChanges' ] = True - response = self._GetResponse( '/rename', request ) - fixit = _ModifiedFilesToFixIt( response[ 'Changes' ], request_data ) - return responses.BuildFixItResponse( [ fixit ] ) - - - def _GoToSymbol( self, request_data, args ): - request = self._DefaultParameters( request_data ) - request.update( { - 'Language': 'C#', - 'Filter': args[ 0 ] - } ) - response = self._GetResponse( '/findsymbols', request ) - - quickfixes = response[ 'QuickFixes' ] - if quickfixes: - if len( quickfixes ) == 1: - ref = quickfixes[ 0 ] - return responses.BuildGoToResponseFromLocation( - _BuildLocation( - request_data, - ref[ 'FileName' ], - ref[ 'Line' ], - ref[ 'Column' ] ), - ref[ 'Text' ] ) - else: - goto_locations = [] - for ref in quickfixes: - goto_locations.append( - responses.BuildGoToResponseFromLocation( - _BuildLocation( request_data, - ref[ 'FileName' ], - ref[ 'Line' ], - ref[ 'Column' ] ), - ref[ 'Text' ] ) ) - - return goto_locations - else: - raise RuntimeError( 'No symbols found' ) - - - def _GoToDocumentOutline( self, request_data ): - request = self._DefaultParameters( request_data ) - response = self._GetResponse( '/currentfilemembersasflat', request ) - if response is not None and len( response ) > 0: - goto_locations = [] - for ref in response: - goto_locations.append( - responses.BuildGoToResponseFromLocation( - _BuildLocation( request_data, - ref[ 'FileName' ], - ref[ 'Line' ], - ref[ 'Column' ] ), - ref[ 'Text' ] ) ) - if len( goto_locations ) > 1: - return goto_locations - return goto_locations[ 0 ] - else: - raise RuntimeError( 'No symbols found' ) - - def _GoToReferences( self, request_data ): - """ Jump to references of identifier under cursor """ - # _GetResponse can throw. Original code by @mispencer - # wrapped it in a try/except and set `reference` to `{ 'QuickFixes': None }` - # After being unable to hit that case with tests, - # that code path was thrown away. - reference = self._GetResponse( - '/findusages', - self._DefaultParameters( request_data ) ) - - quickfixes = reference[ 'QuickFixes' ] - if quickfixes: - if len( quickfixes ) == 1: - ref = quickfixes[ 0 ] - return responses.BuildGoToResponseFromLocation( - _BuildLocation( - request_data, - ref[ 'FileName' ], - ref[ 'Line' ], - ref[ 'Column' ] ) ) - else: - return [ responses.BuildGoToResponseFromLocation( - _BuildLocation( request_data, - ref[ 'FileName' ], - ref[ 'Line' ], - ref[ 'Column' ] ) ) - for ref in quickfixes ] - else: - raise RuntimeError( 'No references found' ) - - - def _GetType( self, request_data ): - request = self._DefaultParameters( request_data ) - request[ "IncludeDocumentation" ] = False - - result = self._GetResponse( '/typelookup', request ) - message = result[ "Type" ] - - if not message: - raise RuntimeError( 'No type info available.' ) - return responses.BuildDisplayMessageResponse( message ) - - - def _OrganizeImports( self, request_data ): - request = self._DefaultParameters( request_data ) - request[ 'WantsTextChanges' ] = True - result = self._GetResponse( '/fixusings', request ) - fixit = responses.FixIt( - _BuildLocation( - request_data, - request_data[ 'filepath' ], - request_data[ 'line_num' ], - request_data[ 'column_codepoint' ] ), - _LinePositionSpanTextChangeToFixItChunks( - result[ 'Changes' ], - request_data[ 'filepath' ], - request_data ) ) - return responses.BuildFixItResponse( [ fixit ] ) - - - def _Format( self, request_data ): - request = self._DefaultParameters( request_data ) - request[ 'WantsTextChanges' ] = True - if 'range' in request_data: - lines = request_data[ 'lines' ] - start = request_data[ 'range' ][ 'start' ] - start_line_num = start[ 'line_num' ] - start_line_value = lines[ start_line_num ] - - start_codepoint = ByteOffsetToCodepointOffset( start_line_value, - start[ 'column_num' ] ) - - end = request_data[ 'range' ][ 'end' ] - end_line_num = end[ 'line_num' ] - end_line_value = lines[ end_line_num ] - end_codepoint = ByteOffsetToCodepointOffset( end_line_value, - end[ 'column_num' ] ) - request.update( { - 'line': start_line_num, - 'column': start_codepoint, - 'EndLine': end_line_num, - 'EndColumn': end_codepoint - } ) - result = self._GetResponse( '/formatRange', request ) - else: - result = self._GetResponse( '/codeformat', request ) - - fixit = responses.FixIt( - _BuildLocation( - request_data, - request_data[ 'filepath' ], - request_data[ 'line_num' ], - request_data[ 'column_codepoint' ] ), - _LinePositionSpanTextChangeToFixItChunks( - result[ 'Changes' ], - request_data[ 'filepath' ], - request_data ) ) - return responses.BuildFixItResponse( [ fixit ] ) - - - def _FixIt( self, request_data ): - request = self._DefaultParameters( request_data ) - request[ 'WantsTextChanges' ] = True - - result = self._GetResponse( '/getcodeactions', request ) - - fixits = [] - for i, code_action_name in enumerate( result[ 'CodeActions' ] ): - fixit = responses.UnresolvedFixIt( { 'index': i }, code_action_name ) - fixits.append( fixit ) - - if len( fixits ) == 1: - fixit = fixits[ 0 ] - fixit = { 'command': fixit.command, 'resolve': fixit.resolve } - return self._ResolveFixIt( request_data, fixit ) - - return responses.BuildFixItResponse( fixits ) - - - def _ResolveFixIt( self, request_data, unresolved_fixit = None ): - fixit = unresolved_fixit if unresolved_fixit else request_data[ 'fixit' ] - if not fixit[ 'resolve' ]: - return { 'fixits': [ fixit ] } - fixit = fixit[ 'command' ] - code_action = fixit[ 'index' ] - request = self._DefaultParameters( request_data ) - request.update( { - 'CodeAction': code_action, - 'WantsTextChanges': True, - } ) - response = self._GetResponse( '/runcodeaction', request ) - fixit = responses.FixIt( - _BuildLocation( - request_data, - request_data[ 'filepath' ], - request_data[ 'line_num' ], - request_data[ 'column_codepoint' ] ), - _LinePositionSpanTextChangeToFixItChunks( - response[ 'Changes' ], - request_data[ 'filepath' ], - request_data ), - response[ 'Text' ] ) - # The sort is necessary to keep the tests stable. - # Python's sort() is stable, so it won't mess up the order within a file. - fixit.chunks.sort( key = lambda c: c.range.start_.filename_ ) - return responses.BuildFixItResponse( [ fixit ] ) - - - def _GetDoc( self, request_data ): - request = self._DefaultParameters( request_data ) - request[ "IncludeDocumentation" ] = True - - result = self._GetResponse( '/typelookup', request ) - message = result.get( 'Type' ) or '' - - if ( result[ "Documentation" ] ): - message += "\n" + result[ "Documentation" ] - - if not message: - raise RuntimeError( 'No documentation available.' ) - return responses.BuildDetailedInfoResponse( message.strip() ) - - - def _DefaultParameters( self, request_data ): - """ Some very common request parameters """ - parameters = {} - parameters[ 'line' ] = request_data[ 'line_num' ] - parameters[ 'column' ] = request_data[ 'column_codepoint' ] - - filepath = request_data[ 'filepath' ] - parameters[ 'buffer' ] = ( - request_data[ 'file_data' ][ filepath ][ 'contents' ] ) - parameters[ 'filename' ] = filepath - return parameters - - - def _ServerIsRunning( self ): - """ Check if our OmniSharp server is running (process is up).""" - return utils.ProcessIsRunning( self._omnisharp_phandle ) - - - def ServerIsHealthy( self ): - """ Check if our OmniSharp server is healthy (up and serving).""" - if not self._ServerIsRunning(): - return False - - try: - return self._GetResponse( '/checkalivestatus', timeout = 3 ) - except Exception: - return False - - - def ServerIsReady( self ): - """ Check if our OmniSharp server is ready (loaded solution file).""" - if not self._ServerIsRunning(): - return False - - try: - return self._GetResponse( '/checkreadystatus', timeout = .2 ) - except Exception: - return False - - - def _ServerLocation( self ): - # We cannot use 127.0.0.1 like we do in other places because OmniSharp - # server only listens on localhost. - return f'http://127.0.0.1:{ self._omnisharp_port }' - - - def _GetResponse( self, handler, parameters = {}, timeout = None ): - """ Handle communication with server """ - target = urljoin( self._ServerLocation(), handler ) - LOGGER.debug( 'TX (%s): %s', handler, parameters ) - try: - response = urllib.request.urlopen( - target, - data = ToBytes( json.dumps( parameters ) ), - timeout = timeout ) - json_response = json.loads( response.read() ) - response.close() - except urllib.error.HTTPError as response: - json_response = json.loads( response.fp.read() ) - response.close() - LOGGER.debug( 'RX: %s', json_response ) - return json_response - - - def _ChooseOmnisharpPort( self ): - if not self._omnisharp_port: - if self._desired_omnisharp_port: - self._omnisharp_port = int( self._desired_omnisharp_port ) - else: - self._omnisharp_port = utils.GetUnusedLocalhostPort() - LOGGER.info( 'using port %s', self._omnisharp_port ) - - -def DiagnosticsToDiagStructure( diagnostics ): - structure = defaultdict( lambda : defaultdict( list ) ) - for diagnostic in diagnostics: - structure[ diagnostic.location_.filename_ ][ - diagnostic.location_.line_number_ ].append( diagnostic ) - return structure - - -def _BuildLocation( request_data, filename, line_num, column_num ): - if line_num <= 0: - return None - # OmniSharp sometimes incorrectly returns 0 for the column number. Assume the - # column is 1 in that case. - if column_num <= 0: - column_num = 1 - contents = GetFileLines( request_data, filename ) - line_value = contents[ min( len( contents ) - 1, line_num - 1 ) ] - return responses.Location( - line_num, - CodepointOffsetToByteOffset( line_value, column_num ), - filename ) - - -def _LinePositionSpanTextChangeToFixItChunks( chunks, filename, request_data ): - return [ responses.FixItChunk( - chunk[ 'NewText' ], - responses.Range( - _BuildLocation( - request_data, - filename, - chunk[ 'StartLine' ], - chunk[ 'StartColumn' ] ), - _BuildLocation( - request_data, - filename, - chunk[ 'EndLine' ], - chunk[ 'EndColumn' ] ) ) ) for chunk in chunks ] - - -def _ModifiedFilesToFixIt( changes, request_data ): - chunks = [] - for change in changes: - chunks.extend( - _LinePositionSpanTextChangeToFixItChunks( - change[ 'Changes' ], - change[ 'FileName' ], - request_data ) ) - # The sort is necessary to keep the tests stable. - # Python's sort() is stable, so it won't mess up the order within a file. - chunks.sort( key = lambda c: c.range.start_.filename_ ) - return responses.FixIt( - _BuildLocation( - request_data, - request_data[ 'filepath' ], - request_data[ 'line_num' ], - request_data[ 'column_codepoint' ] ), - chunks ) + def SupportedFiletypes( self ): + return [ 'cs' ] -def _CsDiagnosticToLspSeverity( diagnostic ): - if diagnostic.kind_ == 'ERROR': - return 1 - if diagnostic.kind_ == 'WARNING': - return 2 - return 3 + def GetType( self, request_data ): + raw_hover = self.GetHoverResponse( request_data ) + value = raw_hover[ 'value' ] + if not value: + raise RuntimeError( 'No type found.' ) + value = value.split( '\n' )[ 1 ] + return responses.BuildDetailedInfoResponse( value ) diff --git a/ycmd/completers/cs/solutiondetection.py b/ycmd/completers/cs/solutiondetection.py deleted file mode 100644 index 8b13035a00..0000000000 --- a/ycmd/completers/cs/solutiondetection.py +++ /dev/null @@ -1,125 +0,0 @@ -# Copyright (C) 2013-2020 ycmd contributors. -# -# This file is part of ycmd. -# -# ycmd 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. -# -# ycmd 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 ycmd. If not, see . - -import os -import glob -from inspect import getfile -from ycmd import extra_conf_store -from ycmd.utils import LOGGER - - -def FindSolutionPath( filepath ): - """Try to find suitable solution file given a source file path using all - available information sources""" - # try to load ycm_extra_conf - # if it needs to be verified, abort here and try again later - module = extra_conf_store.ModuleForSourceFile( filepath ) - path_to_solutionfile = PollModule( module, filepath ) - - if not path_to_solutionfile: - # ycm_extra_conf not available or did not provide a solution file - path_to_solutionfile = GuessFile( filepath ) - - return path_to_solutionfile - - -def PollModule( module, filepath ): - """ Try to use passed module in the selection process by calling - CSharpSolutionFile on it """ - path_to_solutionfile = None - module_hint = None - if module: - try: - module_hint = module.CSharpSolutionFile( filepath ) - LOGGER.info( 'extra_conf_store suggests %s as solution file', - module_hint ) - if module_hint: - # received a full path or one relative to the config's location? - candidates = [ module_hint, - os.path.join( os.path.dirname( getfile( module ) ), - module_hint ) ] - # try the assumptions - for path in candidates: - if os.path.isfile( path ): - # path seems to point to a solution - path_to_solutionfile = path - LOGGER.info( 'Using solution file %s selected by extra_conf_store', - path_to_solutionfile ) - break - except AttributeError: - # the config script might not provide solution file locations - LOGGER.exception( 'Could not retrieve solution for %s' - 'from extra_conf_store', filepath ) - return path_to_solutionfile - - -def GuessFile( filepath ): - """ Find solution files by searching upwards in the file tree """ - tokens = _PathComponents( filepath ) - for i in reversed( range( len( tokens ) - 1 ) ): - path = os.path.join( *tokens[ : i + 1 ] ) - candidates = glob.glob1( path, '*.sln' ) - if len( candidates ) > 0: - # do the whole procedure only for the first solution file(s) you find - return _SolutionTestCheckHeuristics( candidates, tokens, i ) - return None - - -def _SolutionTestCheckHeuristics( candidates, tokens, i ): - """ Test if one of the candidate files stands out """ - path = os.path.join( *tokens[ : i + 1 ] ) - selection = None - # if there is just one file here, use that - if len( candidates ) == 1 : - selection = os.path.join( path, candidates[ 0 ] ) - LOGGER.info( 'Selected solution file %s as it is the first one found', - selection ) - - # there is more than one file, try some hints to decide - # 1. is there a solution named just like the subdirectory with the source? - if ( not selection and i < len( tokens ) - 1 and - f'{ tokens[ i + 1 ] }.sln' in candidates ): - selection = os.path.join( path, f'{ tokens[ i + 1 ] }.sln' ) - LOGGER.info( 'Selected solution file %s as it matches source subfolder', - selection ) - - # 2. is there a solution named just like the directory containing the - # solution? - if not selection and f'{ tokens[ i ] }.sln' in candidates : - selection = os.path.join( path, f'{ tokens[ i ] }.sln' ) - LOGGER.info( 'Selected solution file %s as it matches containing folder', - selection ) - - if not selection: - LOGGER.error( 'Could not decide between multiple solution files:\n%s', - candidates ) - - return selection - - -def _PathComponents( path ): - path_components = [] - while True: - path, folder = os.path.split( path ) - if folder: - path_components.append( folder ) - else: - if path: - path_components.append( path ) - break - path_components.reverse() - return path_components From e4286f0372e90a7e468f93981c267b8291469d49 Mon Sep 17 00:00:00 2001 From: Boris Staletic Date: Sun, 11 Aug 2024 10:36:32 +0200 Subject: [PATCH 2/7] Update to the latest omnisharp-roslyn version - Update to 1.39.12 - Make `mono` optional --- build.py | 50 +++++++++++++++--------------- update_omnisharp.py | 10 +++--- ycmd/completers/cs/cs_completer.py | 23 +++++++++++--- 3 files changed, 49 insertions(+), 34 deletions(-) diff --git a/build.py b/build.py index fba9421cb0..6d038a75a9 100755 --- a/build.py +++ b/build.py @@ -903,44 +903,44 @@ def GetCsCompleterDataForPlatform(): #################################### DATA = { 'win32': { - 'version': 'v1.37.11', + 'version': 'v1.39.12', 'download_url': ( 'https://github.com/OmniSharp/omnisharp-roslyn/release' - 's/download/v1.37.11/omnisharp.http-win-x86.zip' ), - 'file_name': 'omnisharp.http-win-x86.zip', - 'check_sum': ( '461544056b144c97e8413de8c1aa1ddd9e2902f5a9f2223af8046d65' - '4d95f2a0' ), + 's/download/v1.39.12/omnisharp-win-x86.zip' ), + 'file_name': 'omnisharp-win-x86.zip', + 'check_sum': ( '05ffec8d8b72831f9dab73bcaef373e35115ef5a68951c78283cedc5' + 'fb578ddf' ), }, 'win64': { - 'version': 'v1.37.11', + 'version': 'v1.39.12', 'download_url': ( 'https://github.com/OmniSharp/omnisharp-roslyn/release' - 's/download/v1.37.11/omnisharp.http-win-x64.zip' ), - 'file_name': 'omnisharp.http-win-x64.zip', - 'check_sum': ( '7f6f0abfac00d028b90aaf1f56813e4fbb73d84bdf2c4704862aa976' - '1b61a59c' ), + 's/download/v1.39.12/omnisharp-win-x64.zip' ), + 'file_name': 'omnisharp-win-x64.zip', + 'check_sum': ( 'a52562b44c9baa2811f0a617f182a5886bb79bb1532850827d89f173' + 'd8c962b6' ), }, 'macos': { - 'version': 'v1.37.11', + 'version': 'v1.39.12', 'download_url': ( 'https://github.com/OmniSharp/omnisharp-roslyn/release' - 's/download/v1.37.11/omnisharp.http-osx.tar.gz' ), - 'file_name': 'omnisharp.http-osx.tar.gz', - 'check_sum': ( '84b84a8a3cb8fd3986ea795d9230457c43bf130b482fcb77fef57c67' - 'e151828a' ), + 's/download/v1.39.12/omnisharp-osx.tar.gz' ), + 'file_name': 'omnisharp-osx.tar.gz', + 'check_sum': ( '663807abcaa8524b7853803df12e02415376c73bcc93a76eb95d6361' + 'aa5aa59a' ), }, 'linux32': { - 'version': 'v1.37.11', + 'version': 'v1.39.12', 'download_url': ( 'https://github.com/OmniSharp/omnisharp-roslyn/release' - 's/download/v1.37.11/omnisharp.http-linux-x86.tar.gz' ), - 'file_name': 'omnisharp.http-linux-x86.tar.gz', - 'check_sum': ( 'a5ab39380a5d230c75f08bf552980cdc5bd8c31a43348acbfa66f1f4' - '6f12851f' ), + 's/download/v1.39.12/omnisharp-linux-x86.tar.gz' ), + 'file_name': 'omnisharp-linux-x86.tar.gz', + 'check_sum': ( 'd8f58e1f4e2ec68c19674b1d1ca377ee5acc19960802078a93c12919' + '1aafc57a' ), }, 'linux64': { - 'version': 'v1.37.11', + 'version': 'v1.39.12', 'download_url': ( 'https://github.com/OmniSharp/omnisharp-roslyn/release' - 's/download/v1.37.11/omnisharp.http-linux-x64.tar.gz' ), - 'file_name': 'omnisharp.http-linux-x64.tar.gz', - 'check_sum': ( '9a6e9a246babd777229eebb57f0bee86e7ef5da271c67275eae5ed9d' - '7b0ad563' ), + 's/download/v1.39.12/omnisharp-linux-x64.tar.gz' ), + 'file_name': 'omnisharp-linux-x64.tar.gz', + 'check_sum': ( '759b45d4814c052d6dc4f43dbc779f6252dc5c1453556768369ee1fc' + 'dc6d79f5' ), }, } if OnWindows(): diff --git a/update_omnisharp.py b/update_omnisharp.py index 566f186ce4..fe0949d24a 100755 --- a/update_omnisharp.py +++ b/update_omnisharp.py @@ -23,11 +23,11 @@ "releases/{version}/{file_name}" ), } FILE_NAME = { - 'win32': 'omnisharp.http-win-x86.zip', - 'win64': 'omnisharp.http-win-x64.zip', - 'macos': 'omnisharp.http-osx.tar.gz', - 'linux32': 'omnisharp.http-linux-x86.tar.gz', - 'linux64': 'omnisharp.http-linux-x64.tar.gz', + 'win32': 'omnisharp-win-x86.zip', + 'win64': 'omnisharp-win-x64.zip', + 'macos': 'omnisharp-osx.tar.gz', + 'linux32': 'omnisharp-linux-x86.tar.gz', + 'linux64': 'omnisharp-linux-x64.tar.gz', } diff --git a/ycmd/completers/cs/cs_completer.py b/ycmd/completers/cs/cs_completer.py index 4bbd648cb1..f04474a152 100644 --- a/ycmd/completers/cs/cs_completer.py +++ b/ycmd/completers/cs/cs_completer.py @@ -38,6 +38,10 @@ os.path.join( PATH_TO_ROSLYN_OMNISHARP, 'omnisharp', 'OmniSharp.exe' ) ) +def MonoRequired( roslyn_path: str ): + return not utils.OnWindows() and roslyn_path.endswith( '.exe' ) + + def ShouldEnableCsCompleter( user_options ): user_roslyn_path = user_options[ 'roslyn_binary_path' ] if user_roslyn_path and not os.path.isfile( user_roslyn_path ): @@ -49,10 +53,17 @@ def ShouldEnableCsCompleter( user_options ): roslyn = user_roslyn_path else: roslyn = PATH_TO_OMNISHARP_ROSLYN_BINARY - if roslyn: - return True - LOGGER.info( 'No mono executable at %s', mono ) - return False + if not roslyn: + return False + + if MonoRequired( roslyn ): + mono = FindExecutableWithFallback( user_options[ 'mono_binary_path' ], + FindExecutable( 'mono' ) ) + if not mono: + LOGGER.info( 'No mono executable at %s', mono ) + return False + + return True class CsharpCompleter( language_server_completer.LanguageServerCompleter ): @@ -62,6 +73,8 @@ def __init__( self, user_options ): self._roslyn_path = user_options[ 'roslyn_binary_path' ] else: self._roslyn_path = PATH_TO_OMNISHARP_ROSLYN_BINARY + self._mono = FindExecutableWithFallback( user_options[ 'mono_binary_path' ], + FindExecutable( 'mono' ) ) def GetServerName( self ): @@ -77,6 +90,8 @@ def GetCommandLine( self ): cmdline = [ self._roslyn_path, '-lsp' ] if utils.LOGGER.isEnabledFor( logging.DEBUG ): cmdline += [ '-v' ] + if MonoRequired( self._roslyn_path ): + cmdline.insert( 0, self._mono ) return cmdline From 2f18c127c6263376ceb23c8fa417314e1c659c14 Mon Sep 17 00:00:00 2001 From: Boris Staletic Date: Sun, 11 Aug 2024 23:43:48 +0200 Subject: [PATCH 3/7] Attempt to fix C# tests --- build.py | 49 +- update_omnisharp.py | 9 +- ycmd/completers/cs/cs_completer.py | 13 +- ycmd/tests/cs/__init__.py | 111 +- ycmd/tests/cs/debug_info_test.py | 234 +--- ycmd/tests/cs/diagnostics_test.py | 152 +-- ycmd/tests/cs/get_completions_test.py | 188 +-- ycmd/tests/cs/signature_help_test.py | 202 ++-- ycmd/tests/cs/subcommands_test.py | 1071 ++++++++--------- .../not-testy/Program.cs | 12 - .../not-testy/Properties/AssemblyInfo.cs | 22 - .../not-testy/testy.csproj | 43 - .../solution-named-like-folder.sln | 20 - .../solution-named-like-folder/testy.sln | 20 - .../testy/Program.cs | 13 - .../testy/Properties/AssemblyInfo.cs | 22 - .../testy/testy.csproj | 43 - .../solution-named-like-folder/testy2.sln | 20 - .../extra-conf-abs/.ycm_extra_conf.py | 9 - .../extra-conf-abs/testy/Program.cs | 12 - .../testy/Properties/AssemblyInfo.cs | 22 - .../extra-conf-abs/testy/testy.csproj | 43 - .../extra-conf-abs/testy2.sln | 20 - .../extra-conf-bad/testy/.ycm_extra_conf.py | 4 - .../extra-conf-bad/testy/Program.cs | 12 - .../testy/Properties/AssemblyInfo.cs | 22 - .../extra-conf-bad/testy/testy.csproj | 43 - .../extra-conf-bad/testy2.sln | 20 - .../extra-conf-rel/.ycm_extra_conf.py | 4 - .../extra-conf-rel/testy/Program.cs | 12 - .../testy/Properties/AssemblyInfo.cs | 22 - .../extra-conf-rel/testy/testy.csproj | 43 - .../extra-conf-rel/testy/testy2.sln | 20 - .../extra-conf-rel/testy2.sln | 20 - .../testy/Program.cs | 12 - .../testy/Properties/AssemblyInfo.cs | 22 - .../testy/testy.csproj | 43 - .../solution-not-named-like-folder/testy1.sln | 20 - .../solution-not-named-like-folder/testy2.sln | 20 - .../cs/testdata/testy/DiagnosticRange.cs | 8 - ycmd/tests/cs/testdata/testy/Empty.cs | 4 - .../tests/cs/testdata/testy/MaxDiagnostics.cs | 6 - ycmd/tests/cs/testdata/testy/SingleEntity.cs | 3 - .../cs/testdata/testy/ZeroColumnDiagnostic.cs | 3 - ycmd/tests/cs/testdata/testy/testy.csproj | 53 +- .../Program.cs" | 12 - .../Properties/AssemblyInfo.cs" | 22 - .../a project.csproj" | 43 - .../a project.sln" | 20 - .../a project.userprefs" | 13 - 50 files changed, 682 insertions(+), 2194 deletions(-) delete mode 100644 ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder/not-testy/Program.cs delete mode 100644 ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder/not-testy/Properties/AssemblyInfo.cs delete mode 100644 ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder/not-testy/testy.csproj delete mode 100644 ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder/solution-named-like-folder.sln delete mode 100644 ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder/testy.sln delete mode 100644 ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder/testy/Program.cs delete mode 100644 ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder/testy/Properties/AssemblyInfo.cs delete mode 100644 ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder/testy/testy.csproj delete mode 100644 ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder/testy2.sln delete mode 100644 ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-abs/.ycm_extra_conf.py delete mode 100644 ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-abs/testy/Program.cs delete mode 100644 ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-abs/testy/Properties/AssemblyInfo.cs delete mode 100644 ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-abs/testy/testy.csproj delete mode 100644 ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-abs/testy2.sln delete mode 100644 ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-bad/testy/.ycm_extra_conf.py delete mode 100644 ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-bad/testy/Program.cs delete mode 100644 ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-bad/testy/Properties/AssemblyInfo.cs delete mode 100644 ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-bad/testy/testy.csproj delete mode 100644 ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-bad/testy2.sln delete mode 100644 ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-rel/.ycm_extra_conf.py delete mode 100644 ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-rel/testy/Program.cs delete mode 100644 ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-rel/testy/Properties/AssemblyInfo.cs delete mode 100644 ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-rel/testy/testy.csproj delete mode 100644 ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-rel/testy/testy2.sln delete mode 100644 ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-rel/testy2.sln delete mode 100644 ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/testy/Program.cs delete mode 100644 ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/testy/Properties/AssemblyInfo.cs delete mode 100644 ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/testy/testy.csproj delete mode 100644 ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/testy1.sln delete mode 100644 ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/testy2.sln delete mode 100644 ycmd/tests/cs/testdata/testy/DiagnosticRange.cs delete mode 100644 ycmd/tests/cs/testdata/testy/MaxDiagnostics.cs delete mode 100644 ycmd/tests/cs/testdata/testy/ZeroColumnDiagnostic.cs delete mode 100644 "ycmd/tests/cs/testdata/\320\275\320\265\320\277\321\200\320\270\320\273\320\270\321\207\320\275\320\276\320\265 \321\201\320\273\320\276\320\262\320\276/Program.cs" delete mode 100644 "ycmd/tests/cs/testdata/\320\275\320\265\320\277\321\200\320\270\320\273\320\270\321\207\320\275\320\276\320\265 \321\201\320\273\320\276\320\262\320\276/Properties/AssemblyInfo.cs" delete mode 100644 "ycmd/tests/cs/testdata/\320\275\320\265\320\277\321\200\320\270\320\273\320\270\321\207\320\275\320\276\320\265 \321\201\320\273\320\276\320\262\320\276/a project.csproj" delete mode 100644 "ycmd/tests/cs/testdata/\320\275\320\265\320\277\321\200\320\270\320\273\320\270\321\207\320\275\320\276\320\265 \321\201\320\273\320\276\320\262\320\276/a project.sln" delete mode 100644 "ycmd/tests/cs/testdata/\320\275\320\265\320\277\321\200\320\270\320\273\320\270\321\207\320\275\320\276\320\265 \321\201\320\273\320\276\320\262\320\276/a project.userprefs" diff --git a/build.py b/build.py index 6d038a75a9..1f24f0af68 100755 --- a/build.py +++ b/build.py @@ -905,50 +905,43 @@ def GetCsCompleterDataForPlatform(): 'win32': { 'version': 'v1.39.12', 'download_url': ( 'https://github.com/OmniSharp/omnisharp-roslyn/release' - 's/download/v1.39.12/omnisharp-win-x86.zip' ), - 'file_name': 'omnisharp-win-x86.zip', - 'check_sum': ( '05ffec8d8b72831f9dab73bcaef373e35115ef5a68951c78283cedc5' - 'fb578ddf' ), + 's/download/v1.39.12/omnisharp-win-x86-net6.0.zip' ), + 'file_name': 'omnisharp-win-x86-net6.0.zip', + 'check_sum': ( 'b38cfc810bbab7f922130d2c8df266454b7be038ef73d278ae082073' + '58318eec' ), }, 'win64': { 'version': 'v1.39.12', 'download_url': ( 'https://github.com/OmniSharp/omnisharp-roslyn/release' - 's/download/v1.39.12/omnisharp-win-x64.zip' ), - 'file_name': 'omnisharp-win-x64.zip', - 'check_sum': ( 'a52562b44c9baa2811f0a617f182a5886bb79bb1532850827d89f173' - 'd8c962b6' ), + 's/download/v1.39.12/omnisharp-win-x64-net6.0.zip' ), + 'file_name': 'omnisharp-win-x64-net6.0.zip', + 'check_sum': ( '21bb3f7d990b6d464a748e9c11731582caeeaab87d7f749edeacfe13' + '6a09c13e' ), }, 'macos': { 'version': 'v1.39.12', 'download_url': ( 'https://github.com/OmniSharp/omnisharp-roslyn/release' - 's/download/v1.39.12/omnisharp-osx.tar.gz' ), - 'file_name': 'omnisharp-osx.tar.gz', - 'check_sum': ( '663807abcaa8524b7853803df12e02415376c73bcc93a76eb95d6361' - 'aa5aa59a' ), - }, - 'linux32': { - 'version': 'v1.39.12', - 'download_url': ( 'https://github.com/OmniSharp/omnisharp-roslyn/release' - 's/download/v1.39.12/omnisharp-linux-x86.tar.gz' ), - 'file_name': 'omnisharp-linux-x86.tar.gz', - 'check_sum': ( 'd8f58e1f4e2ec68c19674b1d1ca377ee5acc19960802078a93c12919' - '1aafc57a' ), + 's/download/v1.39.12/omnisharp-osx-arm64-net6.0.tar.gz' + '' ), + 'file_name': 'omnisharp-osx-arm64-net6.0.tar.gz', + 'check_sum': ( '27db0ded7bf9b1c90155e01a762ea3a39c7da5c26716211bb367886c' + 'e27b5ac2' ), }, 'linux64': { 'version': 'v1.39.12', 'download_url': ( 'https://github.com/OmniSharp/omnisharp-roslyn/release' - 's/download/v1.39.12/omnisharp-linux-x64.tar.gz' ), - 'file_name': 'omnisharp-linux-x64.tar.gz', - 'check_sum': ( '759b45d4814c052d6dc4f43dbc779f6252dc5c1453556768369ee1fc' - 'dc6d79f5' ), + 's/download/v1.39.12/omnisharp-linux-x64-net6.0.tar.gz' + '' ), + 'file_name': 'omnisharp-linux-x64-net6.0.tar.gz', + 'check_sum': ( 'e6496db73f44005b6c750d5f2da7d752edd181cde7e07062944da816' + '95428f65' ), }, } if OnWindows(): return DATA[ 'win64' if IS_64BIT else 'win32' ] - else: - if OnMac(): - return DATA[ 'macos' ] - return DATA[ 'linux64' if IS_64BIT else 'linux32' ] + if OnMac(): + return DATA[ 'macos' ] + return DATA[ 'linux64' ] def EnableGoCompleter( args ): diff --git a/update_omnisharp.py b/update_omnisharp.py index fe0949d24a..91b541af1b 100755 --- a/update_omnisharp.py +++ b/update_omnisharp.py @@ -23,11 +23,10 @@ "releases/{version}/{file_name}" ), } FILE_NAME = { - 'win32': 'omnisharp-win-x86.zip', - 'win64': 'omnisharp-win-x64.zip', - 'macos': 'omnisharp-osx.tar.gz', - 'linux32': 'omnisharp-linux-x86.tar.gz', - 'linux64': 'omnisharp-linux-x64.tar.gz', + 'win32': 'omnisharp-win-x86-net6.0.zip', + 'win64': 'omnisharp-win-x64-net6.0.zip', + 'macos': 'omnisharp-osx-arm64-net6.0.tar.gz', + 'linux64': 'omnisharp-linux-x64-net6.0.tar.gz', } diff --git a/ycmd/completers/cs/cs_completer.py b/ycmd/completers/cs/cs_completer.py index f04474a152..dbf44351f4 100644 --- a/ycmd/completers/cs/cs_completer.py +++ b/ycmd/completers/cs/cs_completer.py @@ -29,13 +29,12 @@ os.path.abspath( os.path.dirname( __file__ ) ), '..', '..', '..', 'third_party', 'omnisharp-roslyn' ) -PATH_TO_OMNISHARP_ROSLYN_BINARY = os.path.join( - PATH_TO_ROSLYN_OMNISHARP, 'Omnisharp.exe' ) -if ( not os.path.isfile( PATH_TO_OMNISHARP_ROSLYN_BINARY ) - and os.path.isfile( os.path.join( - PATH_TO_ROSLYN_OMNISHARP, 'omnisharp', 'OmniSharp.exe' ) ) ): - PATH_TO_OMNISHARP_ROSLYN_BINARY = ( - os.path.join( PATH_TO_ROSLYN_OMNISHARP, 'omnisharp', 'OmniSharp.exe' ) ) +if utils.OnWindows(): + PATH_TO_OMNISHARP_ROSLYN_BINARY = os.path.join( + PATH_TO_ROSLYN_OMNISHARP, 'OmniSharp.exe' ) +else: + PATH_TO_OMNISHARP_ROSLYN_BINARY = os.path.join( + PATH_TO_ROSLYN_OMNISHARP, 'OmniSharp' ) def MonoRequired( roslyn_path: str ): diff --git a/ycmd/tests/cs/__init__.py b/ycmd/tests/cs/__init__.py index 251421e5ce..3f57dabe3e 100644 --- a/ycmd/tests/cs/__init__.py +++ b/ycmd/tests/cs/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2021 ycmd contributors +# Copyright (C) 2024 ycmd contributors # # This file is part of ycmd. # @@ -17,37 +17,36 @@ import functools import os -import sys -import time -from contextlib import contextmanager from ycmd.tests.test_utils import ( BuildRequest, ClearCompletionsCache, IgnoreExtraConfOutsideTestsFolder, IsolatedApp, - WaitUntilCompleterServerReady, + SetUpApp, StopCompleterServer, - SetUpApp ) + WaitUntilCompleterServerReady ) # noqa shared_app = None -# map of 'app' to filepaths -shared_filepaths = {} -shared_log_indexes = {} - - -def PathToTestFile( *args ): - dir_of_current_script = os.path.dirname( os.path.abspath( __file__ ) ) - return os.path.join( dir_of_current_script, 'testdata', *args ) def setUpModule(): global shared_app shared_app = SetUpApp() + with IgnoreExtraConfOutsideTestsFolder(): + StartCsCompleterServerInDirectory( shared_app, PathToTestFile() ) def tearDownModule(): - global shared_app, shared_filepaths - for filepath in shared_filepaths.get( shared_app, [] ): - StopCompleterServer( shared_app, 'cs', filepath ) + global shared_app + StopCompleterServer( shared_app, 'cs' ) + + +def StartCsCompleterServerInDirectory( app, directory ): + app.post_json( '/event_notification', + BuildRequest( + filepath = os.path.join( directory, 'Empty.cs' ), + event_name = 'FileReadyToParse', + filetype = 'cs' ) ) + WaitUntilCompleterServerReady( app, 'cs' ) def SharedYcmd( test ): @@ -69,81 +68,11 @@ def Wrapper( test_case_instance, *args, **kwargs ): try: test( test_case_instance, app, *args, **kwargs ) finally: - global shared_filepaths - for filepath in shared_filepaths.get( app, [] ): - StopCompleterServer( app, 'cs', filepath ) + StopCompleterServer( app, 'cs' ) return Wrapper return Decorator -def GetDebugInfo( app, filepath ): - request_data = BuildRequest( filetype = 'cs', filepath = filepath ) - return app.post_json( '/debug_info', request_data ).json - - -def ReadFile( filepath, fileposition ): - with open( filepath, encoding = 'utf8' ) as f: - if fileposition: - f.seek( fileposition ) - return f.read(), f.tell() - - -def GetDiagnostics( app, filepath ): - contents, _ = ReadFile( filepath, 0 ) - - event_data = BuildRequest( filepath = filepath, - event_name = 'FileReadyToParse', - filetype = 'cs', - contents = contents ) - - return app.post_json( '/event_notification', event_data ).json - - -@contextmanager -def WrapOmniSharpServer( app, filepath ): - global shared_filepaths - global shared_log_indexes - - if filepath not in shared_filepaths.setdefault( app, [] ): - GetDiagnostics( app, filepath ) - shared_filepaths[ app ].append( filepath ) - WaitUntilCsCompleterIsReady( app, filepath ) - - logfiles = [] - response = GetDebugInfo( app, filepath ) - for server in response[ 'completer' ][ 'servers' ]: - logfiles.extend( server[ 'logfiles' ] ) - - try: - yield - finally: - for logfile in logfiles: - if os.path.isfile( logfile ): - log_content, log_end_position = ReadFile( - logfile, shared_log_indexes.get( logfile, 0 ) ) - shared_log_indexes[ logfile ] = log_end_position - sys.stdout.write( f'Logfile { logfile }:\n\n' ) - sys.stdout.write( log_content ) - sys.stdout.write( '\n' ) - - -def WaitUntilCsCompleterIsReady( app, filepath ): - WaitUntilCompleterServerReady( app, 'cs' ) - # Omnisharp isn't ready when it says it is, so wait until Omnisharp returns - # at least one diagnostic multiple times. - success_count = 0 - for reraise_error in [ False ] * 39 + [ True ]: - try: - if len( GetDiagnostics( app, filepath ) ) == 0: - raise RuntimeError( "No diagnostic" ) - success_count += 1 - if success_count > 2: - break - except Exception: - success_count = 0 - if reraise_error: - raise - - time.sleep( .5 ) - else: - raise RuntimeError( "Never was ready" ) +def PathToTestFile( *args ): + dir_of_current_script = os.path.dirname( os.path.abspath( __file__ ) ) + return os.path.join( dir_of_current_script, 'testdata', *args ) diff --git a/ycmd/tests/cs/debug_info_test.py b/ycmd/tests/cs/debug_info_test.py index f1a4b6fa8b..9e148ec801 100644 --- a/ycmd/tests/cs/debug_info_test.py +++ b/ycmd/tests/cs/debug_info_test.py @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2021 ycmd contributors +# Copyright (C) 2024 ycmd contributors # # This file is part of ycmd. # @@ -15,217 +15,55 @@ # You should have received a copy of the GNU General Public License # along with ycmd. If not, see . -from hamcrest import ( assert_that, contains_exactly, empty, equal_to, - has_entries, has_entry, instance_of ) - -from subprocess import Popen as _mockable_popen - -from unittest.mock import patch +from hamcrest import ( assert_that, + contains_exactly, + has_entries, + has_entry, + has_items, + instance_of ) from unittest import TestCase -from ycmd.completers.cs.cs_completer import PATH_TO_OMNISHARP_ROSLYN_BINARY -from ycmd.completers.cs.hook import GetCompleter from ycmd.tests.cs import setUpModule, tearDownModule # noqa -from ycmd.tests.cs import ( PathToTestFile, - SharedYcmd, - IsolatedYcmd, - WrapOmniSharpServer ) -from ycmd.tests.test_utils import ( BuildRequest, - WaitUntilCompleterServerReady ) -from ycmd import user_options_store -from ycmd.utils import ReadFile - - -def SolutionSelectCheck( app, sourcefile, reference_solution, - extra_conf_store = None ): - # reusable test: verify that the correct solution (reference_solution) is - # detected for a given source file (and optionally a given extra_conf) - if extra_conf_store: - app.post_json( '/load_extra_conf_file', - { 'filepath': extra_conf_store } ) - - result = app.post_json( '/debug_info', - BuildRequest( completer_target = 'filetype_default', - filepath = sourcefile, - filetype = 'cs' ) ).json - - assert_that( - result, - has_entry( 'completer', has_entries( { - 'name': 'C#', - 'servers': contains_exactly( has_entries( { - 'extras': contains_exactly( has_entries( { - 'key': 'solution', - 'value': reference_solution - } ) ) - } ) ) - } ) ) - ) +from ycmd.tests.cs import PathToTestFile, SharedYcmd +from ycmd.tests.test_utils import BuildRequest, is_json_string_matching class DebugInfoTest( TestCase ): @SharedYcmd - def test_DebugInfo_ServerIsRunning( self, app ): - filepath = PathToTestFile( 'testy', 'Program.cs' ) - contents = ReadFile( filepath ) - event_data = BuildRequest( filepath = filepath, - filetype = 'cs', - contents = contents, - event_name = 'FileReadyToParse' ) - - app.post_json( '/event_notification', event_data ) - WaitUntilCompleterServerReady( app, 'cs' ) - - request_data = BuildRequest( filepath = filepath, - filetype = 'cs' ) - assert_that( - app.post_json( '/debug_info', request_data ).json, - has_entry( 'completer', has_entries( { - 'name': 'C#', - 'servers': contains_exactly( has_entries( { - 'name': 'OmniSharp', - 'is_running': True, - 'executable': instance_of( str ), - 'pid': instance_of( int ), - 'address': instance_of( str ), - 'port': instance_of( int ), - 'logfiles': contains_exactly( instance_of( str ), - instance_of( str ) ), - 'extras': contains_exactly( has_entries( { - 'key': 'solution', - 'value': instance_of( str ) - } ) ) - } ) ), - 'items': empty() - } ) ) - ) - - - @SharedYcmd - def test_DebugInfo_ServerIsNotRunning_NoSolution( self, app ): + def test_DebugInfo( self, app ): request_data = BuildRequest( filetype = 'cs' ) assert_that( app.post_json( '/debug_info', request_data ).json, has_entry( 'completer', has_entries( { - 'name': 'C#', + 'name': 'Csharp', 'servers': contains_exactly( has_entries( { - 'name': 'OmniSharp', - 'is_running': False, - 'executable': instance_of( str ), - 'pid': None, + 'name': 'OmniSharp-Roslyn', + 'is_running': instance_of( bool ), + 'executable': contains_exactly( instance_of( str ), + instance_of( str ), + instance_of( str ) ), 'address': None, 'port': None, - 'logfiles': empty() + 'pid': instance_of( int ), + 'logfiles': contains_exactly( instance_of( str ) ), + 'extras': contains_exactly( + has_entries( { + 'key': 'Server State', + 'value': instance_of( str ), + } ), + has_entries( { + 'key': 'Project Directory', + 'value': PathToTestFile(), + } ), + has_entries( { + 'key': 'Open Workspaces', + 'value': has_items() + } ), + has_entries( { + 'key': 'Settings', + 'value': is_json_string_matching( has_entries( {} ) ), + } ), + ) } ) ), - 'items': empty() } ) ) ) - - - @SharedYcmd - def test_DebugInfo_UsesSubfolderHint( self, app ): - SolutionSelectCheck( app, - PathToTestFile( 'testy-multiple-solutions', - 'solution-named-like-folder', - 'testy', 'Program.cs' ), - PathToTestFile( 'testy-multiple-solutions', - 'solution-named-like-folder', - 'testy.sln' ) ) - - - @SharedYcmd - def test_DebugInfo_UsesSuperfolderHint( self, app ): - SolutionSelectCheck( app, - PathToTestFile( 'testy-multiple-solutions', - 'solution-named-like-folder', - 'not-testy', 'Program.cs' ), - PathToTestFile( 'testy-multiple-solutions', - 'solution-named-like-folder', - 'solution-named-like-folder.sln' ) ) - - - @SharedYcmd - def test_DebugInfo_ExtraConfStoreAbsolute( self, app ): - SolutionSelectCheck( app, - PathToTestFile( 'testy-multiple-solutions', - 'solution-not-named-like-folder', - 'extra-conf-abs', - 'testy', 'Program.cs' ), - PathToTestFile( 'testy-multiple-solutions', - 'solution-not-named-like-folder', - 'testy2.sln' ), - PathToTestFile( 'testy-multiple-solutions', - 'solution-not-named-like-folder', - 'extra-conf-abs', - '.ycm_extra_conf.py' ) ) - - - @SharedYcmd - def test_DebugInfo_ExtraConfStoreRelative( self, app ): - SolutionSelectCheck( app, - PathToTestFile( 'testy-multiple-solutions', - 'solution-not-named-like-folder', - 'extra-conf-rel', - 'testy', 'Program.cs' ), - PathToTestFile( 'testy-multiple-solutions', - 'solution-not-named-like-folder', - 'extra-conf-rel', - 'testy2.sln' ), - PathToTestFile( 'testy-multiple-solutions', - 'solution-not-named-like-folder', - 'extra-conf-rel', - '.ycm_extra_conf.py' ) ) - - - @SharedYcmd - def test_DebugInfo_ExtraConfStoreNonexisting( self, app ): - SolutionSelectCheck( app, - PathToTestFile( 'testy-multiple-solutions', - 'solution-not-named-like-folder', - 'extra-conf-bad', - 'testy', 'Program.cs' ), - PathToTestFile( 'testy-multiple-solutions', - 'solution-not-named-like-folder', - 'extra-conf-bad', - 'testy2.sln' ), - PathToTestFile( 'testy-multiple-solutions', - 'solution-not-named-like-folder', - 'extra-conf-bad', - 'testy', '.ycm_extra_conf.py' ) ) - - - def test_GetCompleter_RoslynFound( self ): - assert_that( GetCompleter( user_options_store.GetAll() ) ) - - - @patch( 'ycmd.completers.cs.cs_completer.PATH_TO_OMNISHARP_ROSLYN_BINARY', - None ) - def test_GetCompleter_RoslynNotFound( *args ): - assert_that( not GetCompleter( user_options_store.GetAll() ) ) - - - @patch( 'os.path.isfile', return_value = True ) - @IsolatedYcmd( { 'roslyn_binary_path': 'my_roslyn.exe' } ) - def test_GetCompleter_RoslynFromUserOption( self, app, *args ): - # `@patch` does not play nice with functions defined at class scope - def _popen_mock( cmdline, **kwargs ): - exe_index = 1 if cmdline[ 0 ].endswith( 'mono' ) else 0 - assert_that( cmdline[ exe_index ], equal_to( 'my_roslyn.exe' ) ) - # Need to redirect to real binary to allow test to pass - cmdline[ exe_index ] = PATH_TO_OMNISHARP_ROSLYN_BINARY - return _mockable_popen( cmdline, **kwargs ) - - filepath = PathToTestFile( 'testy', 'Program.cs' ) - with patch( 'subprocess.Popen', wraps = _popen_mock ) as popen_mock: - with WrapOmniSharpServer( app, filepath ): - request = BuildRequest( filepath = filepath, filetype = 'cs' ) - app.post_json( '/debug_info', request ) - - popen_mock.assert_called() - - - @patch( 'os.path.isfile', return_value = False ) - def test_GetCompleter_CustomPathToServer_NotAFile( self, *args ): - user_options = user_options_store.GetAll().copy( - roslyn_binary_path = 'does-not-exist' ) - assert_that( not GetCompleter( user_options ) ) diff --git a/ycmd/tests/cs/diagnostics_test.py b/ycmd/tests/cs/diagnostics_test.py index 9f2fa84700..61cf1f471c 100644 --- a/ycmd/tests/cs/diagnostics_test.py +++ b/ycmd/tests/cs/diagnostics_test.py @@ -15,18 +15,13 @@ # You should have received a copy of the GNU General Public License # along with ycmd. If not, see . -from hamcrest import ( assert_that, contains_exactly, contains_string, equal_to, - has_entries, has_entry, has_items ) +from hamcrest import assert_that, has_entry from unittest import TestCase from ycmd.tests.cs import setUpModule, tearDownModule # noqa -from ycmd.tests.cs import ( IsolatedYcmd, - PathToTestFile, - SharedYcmd, - WrapOmniSharpServer ) +from ycmd.tests.cs import PathToTestFile, SharedYcmd from ycmd.tests.test_utils import ( BuildRequest, - LocationMatcher, - RangeMatcher, + WaitForDiagnosticsToBeReady, WithRetry ) from ycmd.utils import ReadFile @@ -34,133 +29,18 @@ class DiagnosticsTest( TestCase ): @WithRetry() @SharedYcmd - def test_Diagnostics_Basic( self, app ): + def test_Diagnostics_DetailedDiags( self, app ): filepath = PathToTestFile( 'testy', 'Program.cs' ) - with WrapOmniSharpServer( app, filepath ): - contents = ReadFile( filepath ) - - event_data = BuildRequest( filepath = filepath, - event_name = 'FileReadyToParse', - filetype = 'cs', - contents = contents ) - app.post_json( '/event_notification', event_data ) - - diag_data = BuildRequest( filepath = filepath, - filetype = 'cs', - contents = contents, - line_num = 10, - column_num = 2 ) - - results = app.post_json( '/detailed_diagnostic', diag_data ).json - assert_that( results, - has_entry( - 'message', - contains_string( - "Identifier expected" ) ) ) - - - @SharedYcmd - def test_Diagnostics_ZeroBasedLineAndColumn( self, app ): - filepath = PathToTestFile( 'testy', 'Program.cs' ) - with WrapOmniSharpServer( app, filepath ): - contents = ReadFile( filepath ) - - event_data = BuildRequest( filepath = filepath, - event_name = 'FileReadyToParse', - filetype = 'cs', - contents = contents ) - - results = app.post_json( '/event_notification', event_data ).json - - assert_that( results, has_items( - has_entries( { - 'kind': equal_to( 'ERROR' ), - 'text': contains_string( "Identifier expected" ), - 'location': LocationMatcher( filepath, 10, 12 ), - 'location_extent': RangeMatcher( filepath, ( 10, 12 ), ( 10, 12 ) ), - } ) - ) ) - - - @WithRetry() - @SharedYcmd - def test_Diagnostics_WithRange( self, app ): - filepath = PathToTestFile( 'testy', 'DiagnosticRange.cs' ) - with WrapOmniSharpServer( app, filepath ): - contents = ReadFile( filepath ) - - event_data = BuildRequest( filepath = filepath, - event_name = 'FileReadyToParse', - filetype = 'cs', - contents = contents ) - - results = app.post_json( '/event_notification', event_data ).json - - assert_that( results, contains_exactly( - has_entries( { - 'kind': equal_to( 'WARNING' ), - 'text': contains_string( - "The variable '\u4e5d' is assigned but its value is never used" ), - 'location': LocationMatcher( filepath, 6, 13 ), - 'location_extent': RangeMatcher( filepath, ( 6, 13 ), ( 6, 16 ) ) - } ) - ) ) - - - @IsolatedYcmd() - def test_Diagnostics_MultipleSolution( self, app ): - filepaths = [ PathToTestFile( 'testy', 'Program.cs' ), - PathToTestFile( 'testy-multiple-solutions', - 'solution-named-like-folder', - 'testy', 'Program.cs' ) ] - for filepath in filepaths: - with WrapOmniSharpServer( app, filepath ): - contents = ReadFile( filepath ) - event_data = BuildRequest( filepath = filepath, - event_name = 'FileReadyToParse', - filetype = 'cs', - contents = contents ) - - results = app.post_json( '/event_notification', event_data ).json - assert_that( results, has_items( - has_entries( { - 'kind': equal_to( 'ERROR' ), - 'text': contains_string( "Identifier expected" ), - 'location': LocationMatcher( filepath, 10, 12 ), - 'location_extent': RangeMatcher( - filepath, ( 10, 12 ), ( 10, 12 ) ) - } ) - ) ) - - - @IsolatedYcmd( { 'max_diagnostics_to_display': 1 } ) - def test_Diagnostics_MaximumDiagnosticsNumberExceeded( self, app ): - filepath = PathToTestFile( 'testy', 'MaxDiagnostics.cs' ) - with WrapOmniSharpServer( app, filepath ): - contents = ReadFile( filepath ) - - event_data = BuildRequest( filepath = filepath, - event_name = 'FileReadyToParse', + contents = ReadFile( filepath ) + WaitForDiagnosticsToBeReady( app, filepath, contents, 'cs' ) + request_data = BuildRequest( contents = contents, + filepath = filepath, filetype = 'cs', - contents = contents ) - - results = app.post_json( '/event_notification', event_data ).json - - assert_that( results, contains_exactly( - has_entries( { - 'kind': equal_to( 'ERROR' ), - 'text': contains_string( "The type 'MaxDiagnostics' already contains " - "a definition for 'test'" ), - 'location': LocationMatcher( filepath, 4, 16 ), - 'location_extent': RangeMatcher( filepath, ( 4, 16 ), ( 4, 20 ) ) - } ), - has_entries( { - 'kind': equal_to( 'ERROR' ), - 'text': contains_string( 'Maximum number of diagnostics exceeded.' ), - 'location': LocationMatcher( filepath, 1, 1 ), - 'location_extent': RangeMatcher( filepath, ( 1, 1 ), ( 1, 1 ) ), - 'ranges': contains_exactly( - RangeMatcher( filepath, ( 1, 1 ), ( 1, 1 ) ) - ) - } ) - ) ) + line_num = 11, + column_num = 1 ) + + results = app.post_json( '/detailed_diagnostic', request_data ).json + assert_that( results, + has_entry( 'message', + "'Console' does not contain " + "a definition for '' [CS0117]" ) ) diff --git a/ycmd/tests/cs/get_completions_test.py b/ycmd/tests/cs/get_completions_test.py index a992d96bda..83f7e78d76 100644 --- a/ycmd/tests/cs/get_completions_test.py +++ b/ycmd/tests/cs/get_completions_test.py @@ -16,180 +16,44 @@ # along with ycmd. If not, see . from hamcrest import ( assert_that, - calling, empty, has_entries, - has_items, - raises ) + has_items ) from unittest import TestCase -from webtest import AppError from ycmd.tests.cs import setUpModule, tearDownModule # noqa -from ycmd.tests.cs import PathToTestFile, SharedYcmd, WrapOmniSharpServer -from ycmd.tests.test_utils import BuildRequest, CompletionEntryMatcher +from ycmd.tests.cs import PathToTestFile, SharedYcmd +from ycmd.tests.test_utils import ( BuildRequest, + CompletionEntryMatcher, + WithRetry ) from ycmd.utils import ReadFile class GetCompletionsTest( TestCase ): - @SharedYcmd - def test_GetCompletions_DefaultToIdentifier( self, app ): - filepath = PathToTestFile( 'testy', 'Program.cs' ) - with WrapOmniSharpServer( app, filepath ): - contents = ReadFile( filepath ) - - completion_data = BuildRequest( filepath = filepath, - filetype = 'cs', - contents = contents, - line_num = 10, - column_num = 7 ) - response_data = app.post_json( '/completions', completion_data ).json - print( 'Response: ', response_data ) - assert_that( - response_data, - has_entries( { - 'completion_start_column': 4, - 'completions': has_items( - CompletionEntryMatcher( 'Console', '[ID]' ), - ), - 'errors': empty(), - } ) ) - - + @WithRetry @SharedYcmd def test_GetCompletions_Basic( self, app ): filepath = PathToTestFile( 'testy', 'Program.cs' ) - with WrapOmniSharpServer( app, filepath ): - contents = ReadFile( filepath ) - - completion_data = BuildRequest( filepath = filepath, - filetype = 'cs', - contents = contents, - line_num = 10, - column_num = 12 ) - response_data = app.post_json( '/completions', completion_data ).json - print( 'Response: ', response_data ) - assert_that( - response_data, - has_entries( { - 'completion_start_column': 12, - 'completions': has_items( - CompletionEntryMatcher( 'CursorLeft', - 'CursorLeft', - { 'kind': 'Property' } ), - CompletionEntryMatcher( 'CursorSize', - 'CursorSize', - { 'kind': 'Property' } ), - ), - 'errors': empty(), - } ) ) - - - @SharedYcmd - def test_GetCompletions_Unicode( self, app ): - filepath = PathToTestFile( 'testy', 'Unicode.cs' ) - with WrapOmniSharpServer( app, filepath ): - contents = ReadFile( filepath ) - - completion_data = BuildRequest( filepath = filepath, - filetype = 'cs', - contents = contents, - line_num = 43, - column_num = 26 ) - response_data = app.post_json( '/completions', completion_data ).json - assert_that( response_data, - has_entries( { - 'completion_start_column': 26, - 'completions': has_items( - CompletionEntryMatcher( 'DoATest' ), - CompletionEntryMatcher( 'an_int' ), - CompletionEntryMatcher( 'a_unicøde' ), - CompletionEntryMatcher( 'øøø' ), - ), - 'errors': empty(), - } ) ) - - - @SharedYcmd - def test_GetCompletions_MultipleSolution( self, app ): - filepaths = [ PathToTestFile( 'testy', 'Program.cs' ), - PathToTestFile( 'testy-multiple-solutions', - 'solution-named-like-folder', - 'testy', - 'Program.cs' ) ] - for filepath in filepaths: - with WrapOmniSharpServer( app, filepath ): - contents = ReadFile( filepath ) - - completion_data = BuildRequest( filepath = filepath, - filetype = 'cs', - contents = contents, - line_num = 10, - column_num = 12 ) - response_data = app.post_json( '/completions', - completion_data ).json - - print( 'Response: ', response_data ) - assert_that( - response_data, - has_entries( { - 'completion_start_column': 12, - 'completions': has_items( - CompletionEntryMatcher( 'CursorLeft', - 'CursorLeft', - { 'kind': 'Property' } ), - CompletionEntryMatcher( 'CursorSize', - 'CursorSize', - { 'kind': 'Property' } ), - ), - 'errors': empty(), - } ) ) - - - @SharedYcmd - def test_GetCompletions_PathWithSpace( self, app ): - filepath = PathToTestFile( 'неприличное слово', 'Program.cs' ) - with WrapOmniSharpServer( app, filepath ): - contents = ReadFile( filepath ) - - completion_data = BuildRequest( filepath = filepath, - filetype = 'cs', - contents = contents, - line_num = 9, - column_num = 12 ) - response_data = app.post_json( '/completions', completion_data ).json - print( 'Response: ', response_data ) - assert_that( - response_data, - has_entries( { - 'completion_start_column': 12, - 'completions': has_items( - CompletionEntryMatcher( 'CursorLeft', - 'CursorLeft', - { 'kind': 'Property' } ), - CompletionEntryMatcher( 'CursorSize', - 'CursorSize', - { 'kind': 'Property' } ), - ), - 'errors': empty(), - } ) ) - - - @SharedYcmd - def test_GetCompletions_DoesntStartWithAmbiguousMultipleSolutions( - self, app ): - filepath = PathToTestFile( 'testy-multiple-solutions', - 'solution-not-named-like-folder', - 'testy', 'Program.cs' ) contents = ReadFile( filepath ) - event_data = BuildRequest( filepath = filepath, - filetype = 'cs', - contents = contents, - event_name = 'FileReadyToParse' ) + completion_data = BuildRequest( filepath = filepath, + filetype = 'cs', + contents = contents, + line_num = 10, + column_num = 12 ) + response_data = app.post_json( '/completions', completion_data ).json + print( 'Response: ', response_data ) assert_that( - calling( app.post_json ).with_args( '/event_notification', event_data ), - raises( AppError, 'Autodetection of solution file failed' ), - "The Omnisharp server started, despite us not being able to find a " - "suitable solution file to feed it. Did you fiddle with the solution " - "finding code in cs_completer.py? Hopefully you've enhanced it: you need " - "to update this test then :)" ) + response_data, + has_entries( { + 'completion_start_column': 12, + 'completions': has_items( + CompletionEntryMatcher( 'CursorLeft', + None, + { 'kind': 'Property' } ), + CompletionEntryMatcher( 'CursorSize', + None, + { 'kind': 'Property' } ), + ), + 'errors': empty(), + } ) ) diff --git a/ycmd/tests/cs/signature_help_test.py b/ycmd/tests/cs/signature_help_test.py index b7c310fbdf..c6213c1456 100644 --- a/ycmd/tests/cs/signature_help_test.py +++ b/ycmd/tests/cs/signature_help_test.py @@ -18,8 +18,7 @@ from hamcrest import ( assert_that, contains_exactly, empty, - has_entries, - has_items ) + has_entries ) from unittest.mock import patch from unittest import TestCase from ycmd import handlers @@ -27,13 +26,11 @@ from ycmd.tests.cs import setUpModule, tearDownModule # noqa from ycmd.tests.cs import ( PathToTestFile, # noqa IsolatedYcmd, - SharedYcmd, - WrapOmniSharpServer ) + SharedYcmd ) from ycmd.tests.test_utils import ( BuildRequest, ParameterMatcher, SignatureMatcher, - SignatureAvailableMatcher, - CompletionEntryMatcher ) + SignatureAvailableMatcher ) class SignatureHelpTest( TestCase ): @@ -63,21 +60,20 @@ def test_SignatureHelp_TriggerComma( self, app ): filetypes = [ 'cs' ], filepath = filepath, contents = contents ) - with WrapOmniSharpServer( app, filepath ): - response = app.post_json( '/signature_help', request ).json - LOGGER.debug( 'response = %s', response ) - assert_that( response, has_entries( { - 'errors': empty(), - 'signature_help': has_entries( { - 'activeSignature': 0, - 'activeParameter': 1, - 'signatures': contains_exactly( - SignatureMatcher( 'void ContinuousTest.MultiArg(int i, string s)', - [ ParameterMatcher( 29, 34 ), - ParameterMatcher( 36, 44 ) ] ) - ) - } ) - } ) ) + response = app.post_json( '/signature_help', request ).json + LOGGER.debug( 'response = %s', response ) + assert_that( response, has_entries( { + 'errors': empty(), + 'signature_help': has_entries( { + 'activeSignature': 0, + 'activeParameter': 1, + 'signatures': contains_exactly( + SignatureMatcher( 'void ContinuousTest.MultiArg(int i, string s)', + [ ParameterMatcher( 29, 34 ), + ParameterMatcher( 36, 44 ) ] ) + ) + } ) + } ) ) @SharedYcmd @@ -90,20 +86,19 @@ def test_SignatureHelp_TriggerParen( self, app ): filetypes = [ 'cs' ], filepath = filepath, contents = contents ) - with WrapOmniSharpServer( app, filepath ): - response = app.post_json( '/signature_help', request ).json - LOGGER.debug( 'response = %s', response ) - assert_that( response, has_entries( { - 'errors': empty(), - 'signature_help': has_entries( { - 'activeSignature': 0, - 'activeParameter': 0, - 'signatures': contains_exactly( - SignatureMatcher( 'void ContinuousTest.Main(string[] args)', - [ ParameterMatcher( 25, 38 ) ] ) - ) - } ) - } ) ) + response = app.post_json( '/signature_help', request ).json + LOGGER.debug( 'response = %s', response ) + assert_that( response, has_entries( { + 'errors': empty(), + 'signature_help': has_entries( { + 'activeSignature': 0, + 'activeParameter': 0, + 'signatures': contains_exactly( + SignatureMatcher( 'void ContinuousTest.Main(string[] args)', + [ ParameterMatcher( 25, 38 ) ] ) + ) + } ) + } ) ) @IsolatedYcmd( { 'disable_signature_help': True } ) @@ -116,17 +111,16 @@ def test_SignatureHelp_TriggerParen_Disabled( self, app ): filetypes = [ 'cs' ], filepath = filepath, contents = contents ) - with WrapOmniSharpServer( app, filepath ): - response = app.post_json( '/signature_help', request ).json - LOGGER.debug( 'response = %s', response ) - assert_that( response, has_entries( { - 'errors': empty(), - 'signature_help': has_entries( { - 'activeSignature': 0, - 'activeParameter': 0, - 'signatures': empty() - } ) - } ) ) + response = app.post_json( '/signature_help', request ).json + LOGGER.debug( 'response = %s', response ) + assert_that( response, has_entries( { + 'errors': empty(), + 'signature_help': has_entries( { + 'activeSignature': 0, + 'activeParameter': 0, + 'signatures': empty() + } ) + } ) ) @SharedYcmd @@ -139,41 +133,39 @@ def test_SignatureHelp_MultipleSignatures( self, app ): filetypes = [ 'cs' ], filepath = filepath, contents = contents ) - with WrapOmniSharpServer( app, filepath ): - response = app.post_json( '/signature_help', request ).json - LOGGER.debug( 'response = %s', response ) - assert_that( response, has_entries( { - 'errors': empty(), - 'signature_help': has_entries( { - 'activeSignature': 0, - 'activeParameter': 0, - 'signatures': contains_exactly( - SignatureMatcher( 'void ContinuousTest.Overloaded(int i, int a)', - [ ParameterMatcher( 31, 36 ), - ParameterMatcher( 38, 43 ) ] ), - SignatureMatcher( 'void ContinuousTest.Overloaded(string s)', - [ ParameterMatcher( 31, 39 ) ] ), - ) - } ) - } ) ) + response = app.post_json( '/signature_help', request ).json + LOGGER.debug( 'response = %s', response ) + assert_that( response, has_entries( { + 'errors': empty(), + 'signature_help': has_entries( { + 'activeSignature': 0, + 'activeParameter': 0, + 'signatures': contains_exactly( + SignatureMatcher( 'void ContinuousTest.Overloaded(int i, int a)', + [ ParameterMatcher( 31, 36 ), + ParameterMatcher( 38, 43 ) ] ), + SignatureMatcher( 'void ContinuousTest.Overloaded(string s)', + [ ParameterMatcher( 31, 39 ) ] ), + ) + } ) + } ) ) request[ 'column_num' ] = 20 - with WrapOmniSharpServer( app, filepath ): - response = app.post_json( '/signature_help', request ).json - LOGGER.debug( 'response = %s', response ) - assert_that( response, has_entries( { - 'errors': empty(), - 'signature_help': has_entries( { - 'activeSignature': 0, - 'activeParameter': 1, - 'signatures': contains_exactly( - SignatureMatcher( 'void ContinuousTest.Overloaded(int i, int a)', - [ ParameterMatcher( 31, 36 ), - ParameterMatcher( 38, 43 ) ] ), - SignatureMatcher( 'void ContinuousTest.Overloaded(string s)', - [ ParameterMatcher( 31, 39 ) ] ), - ) - } ) - } ) ) + response = app.post_json( '/signature_help', request ).json + LOGGER.debug( 'response = %s', response ) + assert_that( response, has_entries( { + 'errors': empty(), + 'signature_help': has_entries( { + 'activeSignature': 0, + 'activeParameter': 1, + 'signatures': contains_exactly( + SignatureMatcher( 'void ContinuousTest.Overloaded(int i, int a)', + [ ParameterMatcher( 31, 36 ), + ParameterMatcher( 38, 43 ) ] ), + SignatureMatcher( 'void ContinuousTest.Overloaded(string s)', + [ ParameterMatcher( 31, 39 ) ] ), + ) + } ) + } ) ) @SharedYcmd @@ -186,43 +178,13 @@ def test_SignatureHelp_NotAFunction_NoError( self, app ): filetypes = [ 'cs' ], filepath = filepath, contents = contents ) - with WrapOmniSharpServer( app, filepath ): - response = app.post_json( '/signature_help', request ).json - LOGGER.debug( 'response = %s', response ) - assert_that( response, has_entries( { - 'errors': empty(), - 'signature_help': has_entries( { - 'activeSignature': 0, - 'activeParameter': 0, - 'signatures': empty() - } ) - } ) ) - - - @IsolatedYcmd( { 'disable_signature_help': True } ) - def test_GetCompletions_Basic_NoSigHelp( self, app ): - filepath = PathToTestFile( 'testy', 'Program.cs' ) - with WrapOmniSharpServer( app, filepath ): - contents = ReadFile( filepath ) - - completion_data = BuildRequest( filepath = filepath, - filetype = 'cs', - contents = contents, - line_num = 10, - column_num = 12 ) - response_data = app.post_json( '/completions', completion_data ).json - print( 'Response: ', response_data ) - assert_that( - response_data, - has_entries( { - 'completion_start_column': 12, - 'completions': has_items( - CompletionEntryMatcher( 'CursorLeft', - 'CursorLeft', - { 'kind': 'Property' } ), - CompletionEntryMatcher( 'CursorSize', - 'CursorSize', - { 'kind': 'Property' } ), - ), - 'errors': empty(), - } ) ) + response = app.post_json( '/signature_help', request ).json + LOGGER.debug( 'response = %s', response ) + assert_that( response, has_entries( { + 'errors': empty(), + 'signature_help': has_entries( { + 'activeSignature': 0, + 'activeParameter': 0, + 'signatures': empty() + } ) + } ) ) diff --git a/ycmd/tests/cs/subcommands_test.py b/ycmd/tests/cs/subcommands_test.py index 0f40c91d4a..d06511c6b7 100644 --- a/ycmd/tests/cs/subcommands_test.py +++ b/ycmd/tests/cs/subcommands_test.py @@ -29,8 +29,7 @@ from ycmd.tests.cs import setUpModule, tearDownModule # noqa from ycmd.tests.cs import ( IsolatedYcmd, PathToTestFile, - SharedYcmd, - WrapOmniSharpServer ) + SharedYcmd ) from ycmd.tests.test_utils import ( BuildRequest, ChunkMatcher, ErrorMatcher, @@ -39,225 +38,203 @@ RangeMatcher, WaitUntilCompleterServerReady, WithRetry ) -from ycmd.utils import ReadFile +from ycmd.utils import ReadFile, OnWindows def StopServer_KeepLogFiles( app ): filepath = PathToTestFile( 'testy', 'GotoTestCase.cs' ) - with WrapOmniSharpServer( app, filepath ): - event_data = BuildRequest( filetype = 'cs', filepath = filepath ) - - response = app.post_json( '/debug_info', event_data ).json - - logfiles = [] - for server in response[ 'completer' ][ 'servers' ]: - logfiles.extend( server[ 'logfiles' ] ) - - try: - for logfile in logfiles: - assert_that( os.path.exists( logfile ), - f'Logfile should exist at { logfile }' ) - finally: - app.post_json( - '/run_completer_command', - BuildRequest( - filetype = 'cs', - filepath = filepath, - command_arguments = [ 'StopServer' ] - ) + event_data = BuildRequest( filetype = 'cs', filepath = filepath ) + + response = app.post_json( '/debug_info', event_data ).json + + logfiles = [] + for server in response[ 'completer' ][ 'servers' ]: + logfiles.extend( server[ 'logfiles' ] ) + + try: + for logfile in logfiles: + assert_that( os.path.exists( logfile ), + f'Logfile should exist at { logfile }' ) + finally: + app.post_json( + '/run_completer_command', + BuildRequest( + filetype = 'cs', + filepath = filepath, + command_arguments = [ 'StopServer' ] ) + ) - if user_options_store.Value( 'server_keep_logfiles' ): - for logfile in logfiles: - assert_that( os.path.exists( logfile ), - f'Logfile should still exist at { logfile }' ) - else: - for logfile in logfiles: - assert_that( not os.path.exists( logfile ), - f'Logfile should no longer exist at { logfile }' ) + if user_options_store.Value( 'server_keep_logfiles' ): + for logfile in logfiles: + assert_that( os.path.exists( logfile ), + f'Logfile should still exist at { logfile }' ) + else: + for logfile in logfiles: + assert_that( not os.path.exists( logfile ), + f'Logfile should no longer exist at { logfile }' ) class SubcommandsTest( TestCase ): @SharedYcmd def test_Subcommands_FixIt_NoFixitsFound( self, app ): fixit_test = PathToTestFile( 'testy', 'FixItTestCase.cs' ) - with WrapOmniSharpServer( app, fixit_test ): - contents = ReadFile( fixit_test ) + contents = ReadFile( fixit_test ) - request = BuildRequest( completer_target = 'filetype_default', - command_arguments = [ 'FixIt' ], - line_num = 1, - column_num = 1, - contents = contents, - filetype = 'cs', - filepath = fixit_test ) - response = app.post_json( '/run_completer_command', request ).json - assert_that( response, has_entries( { 'fixits': empty() } ) ) + request = BuildRequest( completer_target = 'filetype_default', + command_arguments = [ 'FixIt' ], + line_num = 1, + column_num = 1, + contents = contents, + filetype = 'cs', + filepath = fixit_test ) + response = app.post_json( '/run_completer_command', request ).json + assert_that( response, has_entries( { 'fixits': empty() } ) ) @SharedYcmd def test_Subcommands_FixIt_Multi( self, app ): fixit_test = PathToTestFile( 'testy', 'FixItTestCase.cs' ) - with WrapOmniSharpServer( app, fixit_test ): - contents = ReadFile( fixit_test ) - - request = BuildRequest( completer_target = 'filetype_default', - command_arguments = [ 'FixIt' ], - line_num = 5, - column_num = 27, - contents = contents, - filetype = 'cs', - filepath = fixit_test ) - response = app.post_json( '/run_completer_command', request ).json - assert_that( response, has_entries( { - 'fixits': contains_exactly( - has_entries( { - 'text': 'Introduce constant', - 'command': has_entries( { 'index': 0 } ), - 'resolve': True } ), - has_entries( { - 'text': 'Convert to binary', - 'command': has_entries( { 'index': 1 } ), - 'resolve': True } ), - has_entries( { - 'text': 'Convert to hex', - 'command': has_entries( { 'index': 2 } ), - 'resolve': True } ), - ) } ) ) - request.pop( 'command_arguments' ) - request.update( { 'fixit': response[ 'fixits' ][ 1 ] } ) - response = app.post_json( '/resolve_fixit', request ).json - assert_that( response, has_entries( { - 'fixits': contains_exactly( has_entries( { - 'location': LocationMatcher( fixit_test, 5, 27 ), - 'chunks': contains_exactly( - has_entries( { 'replacement_text': '0b101', } ) ) - } ) ) } ) ) + contents = ReadFile( fixit_test ) + + request = BuildRequest( completer_target = 'filetype_default', + command_arguments = [ 'FixIt' ], + line_num = 5, + column_num = 27, + contents = contents, + filetype = 'cs', + filepath = fixit_test ) + response = app.post_json( '/run_completer_command', request ).json + assert_that( response, has_entries( { + 'fixits': contains_exactly( + has_entries( { + 'text': 'Extract method', + 'resolve': True } ), + has_entries( { + 'text': 'Extract local function', + 'resolve': True } ), + has_entries( { + 'text': 'Introduce parameter for \'5\' ' + '-> and update call sites directly', + 'resolve': True } ), + has_entries( { + 'text': 'Introduce parameter for all occurrences of \'5\' ' + '-> and update call sites directly', + 'resolve': True } ), + has_entries( { + 'text': 'Convert number -> Convert to binary', + 'resolve': True } ), + has_entries( { + 'text': 'Convert number -> Convert to hex', + 'resolve': True } ), + ) } ) ) + request.pop( 'command_arguments' ) + request.update( { 'fixit': response[ 'fixits' ][ 4 ] } ) + response = app.post_json( '/resolve_fixit', request ).json + assert_that( response, has_entries( { + 'fixits': contains_exactly( has_entries( { + 'location': LocationMatcher( fixit_test, 5, 27 ), + 'chunks': contains_exactly( + has_entries( { 'replacement_text': '0b101', } ) ) + } ) ) } ) ) @SharedYcmd def test_Subcommands_FixIt_Range( self, app ): fixit_test = PathToTestFile( 'testy', 'FixItTestCase.cs' ) - with WrapOmniSharpServer( app, fixit_test ): - contents = ReadFile( fixit_test ) - - request = BuildRequest( completer_target = 'filetype_default', - command_arguments = [ 'FixIt' ], - line_num = 5, - column_num = 23, - contents = contents, - filetype = 'cs', - filepath = fixit_test ) - request.update( { 'range': { - 'start': { 'line_num': 5, 'column_num': 23 }, - 'end': { 'line_num': 5, 'column_num': 27 } - } } ) - response = app.post_json( '/run_completer_command', request ).json - assert_that( response, has_entries( { - 'fixits': contains_exactly( - has_entries( { - 'text': 'Extract method', - 'command': has_entries( { 'index': 0 } ), - 'resolve': True } ), - has_entries( { - 'text': 'Extract local function', - 'command': has_entries( { 'index': 1 } ), - 'resolve': True } ), - ) - } ) ) - - - @SharedYcmd - def test_Subcommands_FixIt_Single( self, app ): - fixit_test = PathToTestFile( 'testy', 'FixItTestCase.cs' ) - with WrapOmniSharpServer( app, fixit_test ): - contents = ReadFile( fixit_test ) - - request = BuildRequest( completer_target = 'filetype_default', - command_arguments = [ 'FixIt' ], - line_num = 4, - column_num = 17, - contents = contents, - filetype = 'cs', - filepath = fixit_test ) - response = app.post_json( '/run_completer_command', request ).json - assert_that( response, has_entries( { - 'fixits': contains_exactly( has_entries( { - 'location': LocationMatcher( fixit_test, 4, 17 ), - 'chunks': contains_exactly( - has_entries( { - 'replacement_text': 'var', - 'range': RangeMatcher( fixit_test, ( 4, 13 ), ( 4, 16 ) ) } ) - ) } ) ) } ) ) + contents = ReadFile( fixit_test ) + + request = BuildRequest( completer_target = 'filetype_default', + command_arguments = [ 'FixIt' ], + line_num = 5, + column_num = 23, + contents = contents, + filetype = 'cs', + filepath = fixit_test ) + request.update( { 'range': { + 'start': { 'line_num': 5, 'column_num': 23 }, + 'end': { 'line_num': 5, 'column_num': 27 } + } } ) + response = app.post_json( '/run_completer_command', request ).json + assert_that( response, has_entries( { + 'fixits': contains_exactly( + has_entries( { + 'text': 'Remove unused variable', + 'resolve': True } ), + has_entries( { + 'text': 'Extract method', + 'resolve': True } ), + has_entries( { + 'text': 'Extract local function', + 'resolve': True } ), + ) + } ) ) @SharedYcmd def test_Subcommands_RefactorRename_MissingNewName( self, app ): continuous_test = PathToTestFile( 'testy', 'ContinuousTest.cs' ) - with WrapOmniSharpServer( app, continuous_test ): - contents = ReadFile( continuous_test ) - - request = BuildRequest( completer_target = 'filetype_default', - command_arguments = [ 'RefactorRename' ], - line_num = 5, - column_num = 15, - contents = contents, - filetype = 'cs', - filepath = continuous_test ) - response = app.post_json( '/run_completer_command', - request, - expect_errors = True ).json - assert_that( response, ErrorMatcher( ValueError, - 'Please specify a new name to rename it to.\n' - 'Usage: RefactorRename ' ) ) + contents = ReadFile( continuous_test ) + + request = BuildRequest( completer_target = 'filetype_default', + command_arguments = [ 'RefactorRename' ], + line_num = 5, + column_num = 15, + contents = contents, + filetype = 'cs', + filepath = continuous_test ) + response = app.post_json( '/run_completer_command', + request, + expect_errors = True ).json + assert_that( response, ErrorMatcher( ValueError, + 'Please specify a new name to rename it to.\n' + 'Usage: RefactorRename ' ) ) @SharedYcmd def test_Subcommands_RefactorRename_Unicode( self, app ): unicode_test = PathToTestFile( 'testy', 'Unicode.cs' ) - with WrapOmniSharpServer( app, unicode_test ): - contents = ReadFile( unicode_test ) - - request = BuildRequest( completer_target = 'filetype_default', - command_arguments = [ 'RefactorRename', 'x' ], - line_num = 30, - column_num = 31, - contents = contents, - filetype = 'cs', - filepath = unicode_test ) - response = app.post_json( '/run_completer_command', request ).json - assert_that( response, has_entries( { - 'fixits': contains_exactly( has_entries( { - 'location': LocationMatcher( unicode_test, 30, 31 ), - 'chunks': contains_exactly( - has_entries( { - 'replacement_text': 'x', - 'range': RangeMatcher( unicode_test, ( 30, 29 ), ( 30, 35 ) ) } ) - ) } ) ) } ) ) + contents = ReadFile( unicode_test ) + + request = BuildRequest( completer_target = 'filetype_default', + command_arguments = [ 'RefactorRename', 'x' ], + line_num = 30, + column_num = 31, + contents = contents, + filetype = 'cs', + filepath = unicode_test ) + response = app.post_json( '/run_completer_command', request ).json + assert_that( response, has_entries( { + 'fixits': contains_exactly( has_entries( { + 'location': LocationMatcher( unicode_test, 30, 31 ), + 'chunks': contains_exactly( + has_entries( { + 'replacement_text': 'x', + 'range': RangeMatcher( unicode_test, ( 30, 29 ), ( 30, 35 ) ) } ) + ) } ) ) } ) ) @SharedYcmd def test_Subcommands_RefactorRename_Basic( self, app ): continuous_test = PathToTestFile( 'testy', 'ContinuousTest.cs' ) - with WrapOmniSharpServer( app, continuous_test ): - contents = ReadFile( continuous_test ) - - request = BuildRequest( completer_target = 'filetype_default', - command_arguments = [ 'RefactorRename', 'x' ], - line_num = 5, - column_num = 15, - contents = contents, - filetype = 'cs', - filepath = continuous_test ) - response = app.post_json( '/run_completer_command', request ).json - assert_that( response, has_entries( { - 'fixits': contains_exactly( has_entries( { - 'location': LocationMatcher( continuous_test, 5, 15 ), - 'chunks': contains_exactly( - has_entries( { - 'replacement_text': 'x', - 'range': RangeMatcher( continuous_test, ( 5, 15 ), ( 5, 29 ) ) } ) - ) } ) ) } ) ) + contents = ReadFile( continuous_test ) + + request = BuildRequest( completer_target = 'filetype_default', + command_arguments = [ 'RefactorRename', 'x' ], + line_num = 5, + column_num = 15, + contents = contents, + filetype = 'cs', + filepath = continuous_test ) + response = app.post_json( '/run_completer_command', request ).json + assert_that( response, has_entries( { + 'fixits': contains_exactly( has_entries( { + 'location': LocationMatcher( continuous_test, 5, 15 ), + 'chunks': contains_exactly( + has_entries( { + 'replacement_text': 'x', + 'range': RangeMatcher( continuous_test, ( 5, 15 ), ( 5, 29 ) ) } ) + ) } ) ) } ) ) @WithRetry() @@ -306,399 +283,260 @@ def test_Subcommands_GoToSymbol( self, app ): PathToTestFile( 'testy', 'GotoTestCase.cs' ), 44, 15 ), LocationMatcher( PathToTestFile( 'testy', 'GotoTestCase.cs' ), 49, 15 ) ) ), - ( 'asd', ErrorMatcher( RuntimeError, 'No symbols found' ) ) + ( 'asd', ErrorMatcher( RuntimeError, 'Symbol not found' ) ) ]: with self.subTest( identifier = identifier, expected = expected ): filepath = PathToTestFile( 'testy', 'GotoTestCase.cs' ) - with WrapOmniSharpServer( app, filepath ): - contents = ReadFile( filepath ) - goto_data = BuildRequest( completer_target = 'filetype_default', - command_arguments = [ 'GoToSymbol', - identifier ], - line_num = 1, - column_num = 1, - contents = contents, - filetype = 'cs', - filepath = filepath ) - response = app.post_json( '/run_completer_command', - goto_data, - expect_errors = True ).json - assert_that( response, expected ) + contents = ReadFile( filepath ) + goto_data = BuildRequest( completer_target = 'filetype_default', + command_arguments = [ 'GoToSymbol', + identifier ], + line_num = 1, + column_num = 1, + contents = contents, + filetype = 'cs', + filepath = filepath ) + response = app.post_json( '/run_completer_command', + goto_data, + expect_errors = True ).json + assert_that( response, expected ) @SharedYcmd def test_Subcommands_GoTo_Unicode( self, app ): filepath = PathToTestFile( 'testy', 'Unicode.cs' ) - with WrapOmniSharpServer( app, filepath ): - contents = ReadFile( filepath ) - - goto_data = BuildRequest( completer_target = 'filetype_default', - command_arguments = [ 'GoTo' ], - line_num = 45, - column_num = 43, - contents = contents, - filetype = 'cs', - filepath = filepath ) + contents = ReadFile( filepath ) + + goto_data = BuildRequest( completer_target = 'filetype_default', + command_arguments = [ 'GoTo' ], + line_num = 45, + column_num = 43, + contents = contents, + filetype = 'cs', + filepath = filepath ) - response = app.post_json( '/run_completer_command', goto_data ).json - assert_that( response, LocationMatcher( filepath, 30, 54 ) ) + response = app.post_json( '/run_completer_command', goto_data ).json + assert_that( response, LocationMatcher( filepath, 30, 54 ) ) @SharedYcmd def test_Subcommands_GoToImplementation_Basic( self, app ): filepath = PathToTestFile( 'testy', 'GotoTestCase.cs' ) - with WrapOmniSharpServer( app, filepath ): - contents = ReadFile( filepath ) - - goto_data = BuildRequest( - completer_target = 'filetype_default', - command_arguments = [ 'GoToImplementation' ], - line_num = 14, - column_num = 13, - contents = contents, - filetype = 'cs', - filepath = filepath - ) - - response = app.post_json( '/run_completer_command', goto_data ).json - assert_that( response, LocationMatcher( filepath, 31, 15 ) ) - + contents = ReadFile( filepath ) - @SharedYcmd - def test_Subcommands_GoToImplementation_NoImplementation( self, app ): - filepath = PathToTestFile( 'testy', 'GotoTestCase.cs' ) - with WrapOmniSharpServer( app, filepath ): - contents = ReadFile( filepath ) - - goto_data = BuildRequest( - completer_target = 'filetype_default', - command_arguments = [ 'GoToImplementation' ], - line_num = 18, - column_num = 13, - contents = contents, - filetype = 'cs', - filepath = filepath - ) + goto_data = BuildRequest( + completer_target = 'filetype_default', + command_arguments = [ 'GoToImplementation' ], + line_num = 14, + column_num = 13, + contents = contents, + filetype = 'cs', + filepath = filepath + ) - response = app.post_json( '/run_completer_command', - goto_data, - expect_errors = True ).json - assert_that( response, ErrorMatcher( RuntimeError, - 'No implementations found' ) ) + response = app.post_json( '/run_completer_command', goto_data ).json + assert_that( response, LocationMatcher( filepath, 31, 15 ) ) @SharedYcmd - def test_Subcommands_CsCompleter_InvalidLocation( self, app ): + def test_Subcommands_GoToImplementation_NoImplementation( self, app ): filepath = PathToTestFile( 'testy', 'GotoTestCase.cs' ) - with WrapOmniSharpServer( app, filepath ): - contents = ReadFile( filepath ) - - goto_data = BuildRequest( - completer_target = 'filetype_default', - command_arguments = [ 'GoToImplementation' ], - line_num = 3, - column_num = 1, - contents = contents, - filetype = 'cs', - filepath = filepath - ) - - response = app.post_json( '/run_completer_command', - goto_data, - expect_errors = True ).json - assert_that( response, ErrorMatcher( RuntimeError, - "Can't jump to implementation" ) ) - + contents = ReadFile( filepath ) - @SharedYcmd - def test_Subcommands_GoToImplementationElseDeclaration_NoImplementation( - self, app ): - filepath = PathToTestFile( 'testy', 'GotoTestCase.cs' ) - with WrapOmniSharpServer( app, filepath ): - contents = ReadFile( filepath ) - - goto_data = BuildRequest( - completer_target = 'filetype_default', - command_arguments = [ 'GoToImplementationElseDeclaration' ], - line_num = 18, - column_num = 13, - contents = contents, - filetype = 'cs', - filepath = filepath - ) + goto_data = BuildRequest( + completer_target = 'filetype_default', + command_arguments = [ 'GoToImplementation' ], + line_num = 18, + column_num = 13, + contents = contents, + filetype = 'cs', + filepath = filepath + ) - response = app.post_json( '/run_completer_command', goto_data ).json - assert_that( response, LocationMatcher( filepath, 36, 8 ) ) + response = app.post_json( '/run_completer_command', + goto_data, + expect_errors = True ).json + assert_that( response, ErrorMatcher( RuntimeError, + 'Cannot jump to location' ) ) @SharedYcmd - def test_Subcommands_GoToImplementationElseDeclaration_SingleImplementation( - self, app ): + def test_Subcommands_CsCompleter_InvalidLocation( self, app ): filepath = PathToTestFile( 'testy', 'GotoTestCase.cs' ) - with WrapOmniSharpServer( app, filepath ): - contents = ReadFile( filepath ) - - goto_data = BuildRequest( - completer_target = 'filetype_default', - command_arguments = [ 'GoToImplementationElseDeclaration' ], - line_num = 14, - column_num = 13, - contents = contents, - filetype = 'cs', - filepath = filepath - ) - - response = app.post_json( '/run_completer_command', goto_data ).json - assert_that( response, LocationMatcher( filepath, 31, 15 ) ) - + contents = ReadFile( filepath ) - @SharedYcmd - def test_Subcommands_GoToImplementationElseDeclaration_Multiple( - self, app ): - filepath = PathToTestFile( 'testy', 'GotoTestCase.cs' ) - with WrapOmniSharpServer( app, filepath ): - contents = ReadFile( filepath ) - - goto_data = BuildRequest( - completer_target = 'filetype_default', - command_arguments = [ 'GoToImplementationElseDeclaration' ], - line_num = 22, - column_num = 13, - contents = contents, - filetype = 'cs', - filepath = filepath - ) + goto_data = BuildRequest( + completer_target = 'filetype_default', + command_arguments = [ 'GoToImplementation' ], + line_num = 3, + column_num = 1, + contents = contents, + filetype = 'cs', + filepath = filepath + ) - response = app.post_json( '/run_completer_command', goto_data ).json - assert_that( response, - contains_exactly( LocationMatcher( filepath, 44, 15 ), - LocationMatcher( filepath, 49, 15 ) ) ) + response = app.post_json( '/run_completer_command', + goto_data, + expect_errors = True ).json + assert_that( response, ErrorMatcher( RuntimeError, + 'Cannot jump to location' ) ) @SharedYcmd def test_Subcommands_GoToReferences_InvalidLocation( self, app ): filepath = PathToTestFile( 'testy', 'GotoTestCase.cs' ) - with WrapOmniSharpServer( app, filepath ): - contents = ReadFile( filepath ) - - goto_data = BuildRequest( - completer_target = 'filetype_default', - command_arguments = [ 'GoToReferences' ], - line_num = 3, - column_num = 1, - contents = contents, - filetype = 'cs', - filepath = filepath - ) + contents = ReadFile( filepath ) - response = app.post_json( '/run_completer_command', - goto_data, - expect_errors = True ).json - assert_that( response, ErrorMatcher( - RuntimeError, 'No references found' ) ) + goto_data = BuildRequest( + completer_target = 'filetype_default', + command_arguments = [ 'GoToReferences' ], + line_num = 3, + column_num = 1, + contents = contents, + filetype = 'cs', + filepath = filepath + ) + + response = app.post_json( '/run_completer_command', + goto_data, + expect_errors = True ).json + assert_that( response, ErrorMatcher( + RuntimeError, 'Cannot jump to location' ) ) @SharedYcmd def test_Subcommands_GoToReferences_MultipleReferences( self, app ): filepath = PathToTestFile( 'testy', 'GotoTestCase.cs' ) - with WrapOmniSharpServer( app, filepath ): - contents = ReadFile( filepath ) - - goto_data = BuildRequest( - completer_target = 'filetype_default', - command_arguments = [ 'GoToReferences' ], - line_num = 18, - column_num = 4, - contents = contents, - filetype = 'cs', - filepath = filepath - ) + contents = ReadFile( filepath ) + + goto_data = BuildRequest( + completer_target = 'filetype_default', + command_arguments = [ 'GoToReferences' ], + line_num = 18, + column_num = 4, + contents = contents, + filetype = 'cs', + filepath = filepath + ) - response = app.post_json( '/run_completer_command', goto_data ).json - assert_that( response, - contains_exactly( LocationMatcher( filepath, 17, 54 ), - LocationMatcher( filepath, 18, 4 ) ) ) + response = app.post_json( '/run_completer_command', goto_data ).json + assert_that( response, + contains_exactly( LocationMatcher( filepath, 17, 54 ), + LocationMatcher( filepath, 18, 4 ) ) ) @SharedYcmd def test_Subcommands_GoToReferences_Basic( self, app ): filepath = PathToTestFile( 'testy', 'GotoTestCase.cs' ) - with WrapOmniSharpServer( app, filepath ): - contents = ReadFile( filepath ) - - goto_data = BuildRequest( - completer_target = 'filetype_default', - command_arguments = [ 'GoToReferences' ], - line_num = 21, - column_num = 29, - contents = contents, - filetype = 'cs', - filepath = filepath - ) + contents = ReadFile( filepath ) - response = app.post_json( '/run_completer_command', goto_data ).json - assert_that( response, LocationMatcher( filepath, 21, 15 ) ) + goto_data = BuildRequest( + completer_target = 'filetype_default', + command_arguments = [ 'GoToReferences' ], + line_num = 21, + column_num = 29, + contents = contents, + filetype = 'cs', + filepath = filepath + ) + + response = app.post_json( '/run_completer_command', goto_data ).json + assert_that( response, LocationMatcher( filepath, 21, 15 ) ) @SharedYcmd def test_Subcommands_GetToImplementation_Unicode( self, app ): filepath = PathToTestFile( 'testy', 'Unicode.cs' ) - with WrapOmniSharpServer( app, filepath ): - contents = ReadFile( filepath ) - - goto_data = BuildRequest( - completer_target = 'filetype_default', - command_arguments = [ 'GoToImplementation' ], - line_num = 48, - column_num = 44, - contents = contents, - filetype = 'cs', - filepath = filepath - ) + contents = ReadFile( filepath ) + + goto_data = BuildRequest( + completer_target = 'filetype_default', + command_arguments = [ 'GoToImplementation' ], + line_num = 48, + column_num = 44, + contents = contents, + filetype = 'cs', + filepath = filepath + ) - response = app.post_json( '/run_completer_command', goto_data ).json - assert_that( response, - contains_exactly( LocationMatcher( filepath, 49, 66 ), - LocationMatcher( filepath, 50, 62 ) ) ) + response = app.post_json( '/run_completer_command', goto_data ).json + assert_that( response, + contains_exactly( LocationMatcher( filepath, 49, 66 ), + LocationMatcher( filepath, 50, 62 ) ) ) @SharedYcmd def test_Subcommands_GetType_EmptyMessage( self, app ): filepath = PathToTestFile( 'testy', 'GetTypeTestCase.cs' ) - with WrapOmniSharpServer( app, filepath ): - contents = ReadFile( filepath ) + contents = ReadFile( filepath ) - gettype_data = BuildRequest( completer_target = 'filetype_default', - command_arguments = [ 'GetType' ], - line_num = 1, - column_num = 1, - contents = contents, - filetype = 'cs', - filepath = filepath ) + gettype_data = BuildRequest( completer_target = 'filetype_default', + command_arguments = [ 'GetType' ], + line_num = 1, + column_num = 1, + contents = contents, + filetype = 'cs', + filepath = filepath ) - response = app.post_json( '/run_completer_command', - gettype_data, - expect_errors = True ).json - assert_that( response, ErrorMatcher( RuntimeError, - 'No type info available.' ) ) + response = app.post_json( '/run_completer_command', + gettype_data, + expect_errors = True ).json + assert_that( response, ErrorMatcher( RuntimeError, + 'No type found.' ) ) @SharedYcmd def test_Subcommands_GetType_VariableDeclaration( self, app ): filepath = PathToTestFile( 'testy', 'GetTypeTestCase.cs' ) - with WrapOmniSharpServer( app, filepath ): - contents = ReadFile( filepath ) + contents = ReadFile( filepath ) - gettype_data = BuildRequest( completer_target = 'filetype_default', - command_arguments = [ 'GetType' ], - line_num = 5, - column_num = 5, - contents = contents, - filetype = 'cs', - filepath = filepath ) + gettype_data = BuildRequest( completer_target = 'filetype_default', + command_arguments = [ 'GetType' ], + line_num = 5, + column_num = 9, + contents = contents, + filetype = 'cs', + filepath = filepath ) - response = app.post_json( '/run_completer_command', gettype_data ).json - assert_that( response, has_entry( 'message', 'System.String' ) ) + response = app.post_json( '/run_completer_command', gettype_data ).json + assert_that( response, has_entry( 'detailed_info', + '(local variable) string str' ) ) @SharedYcmd def test_Subcommands_GetType_VariableUsage( self, app ): filepath = PathToTestFile( 'testy', 'GetTypeTestCase.cs' ) - with WrapOmniSharpServer( app, filepath ): - contents = ReadFile( filepath ) + contents = ReadFile( filepath ) - gettype_data = BuildRequest( completer_target = 'filetype_default', - command_arguments = [ 'GetType' ], - line_num = 6, - column_num = 5, - contents = contents, - filetype = 'cs', - filepath = filepath ) + gettype_data = BuildRequest( completer_target = 'filetype_default', + command_arguments = [ 'GetType' ], + line_num = 6, + column_num = 5, + contents = contents, + filetype = 'cs', + filepath = filepath ) - response = app.post_json( '/run_completer_command', gettype_data ).json - assert_that( response, has_entry( 'message', 'string str' ) ) + response = app.post_json( '/run_completer_command', gettype_data ).json + assert_that( response, has_entry( 'detailed_info', + '(local variable) string str' ) ) @SharedYcmd def test_Subcommands_GetType_DocsIgnored( self, app ): filepath = PathToTestFile( 'testy', 'GetTypeTestCase.cs' ) - with WrapOmniSharpServer( app, filepath ): - contents = ReadFile( filepath ) - - gettype_data = BuildRequest( completer_target = 'filetype_default', - command_arguments = [ 'GetType' ], - line_num = 10, - column_num = 34, - contents = contents, - filetype = 'cs', - filepath = filepath ) - - response = app.post_json( '/run_completer_command', gettype_data ).json - assert_that( response, has_entry( - 'message', 'int GetTypeTestCase.an_int_with_docs' ) ) - - - @SharedYcmd - def test_Subcommands_GetDoc_Invalid( self, app ): - filepath = PathToTestFile( 'testy', 'GetDocTestCase.cs' ) - with WrapOmniSharpServer( app, filepath ): - contents = ReadFile( filepath ) - - getdoc_data = BuildRequest( completer_target = 'filetype_default', - command_arguments = [ 'GetDoc' ], - line_num = 1, - column_num = 1, - contents = contents, - filetype = 'cs', - filepath = filepath ) - - response = app.post_json( '/run_completer_command', - getdoc_data, - expect_errors = True ).json - assert_that( response, ErrorMatcher( RuntimeError, - 'No documentation available.' ) ) - - - @SharedYcmd - def test_Subcommands_GetDoc_Variable( self, app ): - filepath = PathToTestFile( 'testy', 'GetDocTestCase.cs' ) - with WrapOmniSharpServer( app, filepath ): - contents = ReadFile( filepath ) - - getdoc_data = BuildRequest( completer_target = 'filetype_default', - command_arguments = [ 'GetDoc' ], - line_num = 13, - column_num = 28, - contents = contents, - filetype = 'cs', - filepath = filepath ) + contents = ReadFile( filepath ) - response = app.post_json( '/run_completer_command', getdoc_data ).json - assert_that( response, - has_entry( 'detailed_info', - 'int GetDocTestCase.an_int\n' - 'an integer, or something' ) ) + gettype_data = BuildRequest( completer_target = 'filetype_default', + command_arguments = [ 'GetType' ], + line_num = 10, + column_num = 34, + contents = contents, + filetype = 'cs', + filepath = filepath ) - - @SharedYcmd - def test_Subcommands_GetDoc_Function( self, app ): - filepath = PathToTestFile( 'testy', 'GetDocTestCase.cs' ) - with WrapOmniSharpServer( app, filepath ): - contents = ReadFile( filepath ) - - getdoc_data = BuildRequest( completer_target = 'filetype_default', - command_arguments = [ 'GetDoc' ], - line_num = 33, - column_num = 27, - contents = contents, - filetype = 'cs', - filepath = filepath ) - - response = app.post_json( '/run_completer_command', getdoc_data ).json - assert_that( response, has_entry( 'detailed_info', - 'int GetDocTestCase.DoATest()\n' - 'Very important method.\n\nWith multiple lines of ' - 'commentary\nAnd Format-\n-ting' ) ) + response = app.post_json( '/run_completer_command', gettype_data ).json + assert_that( response, has_entry( + 'detailed_info', '(field) int GetTypeTestCase.an_int_with_docs' ) ) @IsolatedYcmd() @@ -737,28 +575,27 @@ def test_Subcommands_StopServer_DoNotKeepLogFiles( self, app ): @IsolatedYcmd() def test_Subcommands_RestartServer_PidChanges( self, app ): filepath = PathToTestFile( 'testy', 'GotoTestCase.cs' ) - with WrapOmniSharpServer( app, filepath ): - - def GetPid(): - request_data = BuildRequest( filetype = 'cs', filepath = filepath ) - debug_info = app.post_json( '/debug_info', request_data ).json - return debug_info[ "completer" ][ "servers" ][ 0 ][ "pid" ] - - old_pid = GetPid() - - app.post_json( - '/run_completer_command', - BuildRequest( - filetype = 'cs', - filepath = filepath, - command_arguments = [ 'RestartServer' ] - ) + + def GetPid(): + request_data = BuildRequest( filetype = 'cs', filepath = filepath ) + debug_info = app.post_json( '/debug_info', request_data ).json + return debug_info[ "completer" ][ "servers" ][ 0 ][ "pid" ] + + old_pid = GetPid() + + app.post_json( + '/run_completer_command', + BuildRequest( + filetype = 'cs', + filepath = filepath, + command_arguments = [ 'RestartServer' ] ) - WaitUntilCompleterServerReady( app, 'cs' ) + ) + WaitUntilCompleterServerReady( app, 'cs' ) - new_pid = GetPid() + new_pid = GetPid() - assert old_pid != new_pid, '%r == %r' % ( old_pid, new_pid ) + assert old_pid != new_pid, '%r == %r' % ( old_pid, new_pid ) @IsolatedYcmd() @@ -797,18 +634,53 @@ def test_Subcommands_StopServer_Timeout( self, app ): @SharedYcmd def test_Subcommands_Format_Works( self, app ): filepath = PathToTestFile( 'testy', 'Program.cs' ) - with WrapOmniSharpServer( app, filepath ): - contents = ReadFile( filepath ) - - request = BuildRequest( command_arguments = [ 'Format' ], - line_num = 1, - column_num = 1, - contents = contents, - filetype = 'cs', - filepath = filepath ) + contents = ReadFile( filepath ) - response = app.post_json( '/run_completer_command', request ).json - print( 'completer response = ', response ) + request = BuildRequest( command_arguments = [ 'Format' ], + line_num = 1, + column_num = 1, + contents = contents, + filetype = 'cs', + filepath = filepath ) + + request[ 'options' ] = { + 'tab_size': 2, + 'insert_spaces': True + } + response = app.post_json( '/run_completer_command', request ).json + print( 'completer response = ', response ) + if OnWindows(): + assert_that( response, has_entries( { + 'fixits': contains_exactly( has_entries( { + 'location': LocationMatcher( filepath, 1, 1 ), + 'chunks': contains_exactly( + ChunkMatcher( + '\n }\n ', + LocationMatcher( filepath, 11, 1 ), + LocationMatcher( filepath, 12, 2 ) + ), + ChunkMatcher( + '\n ', + LocationMatcher( filepath, 9, 16 ), + LocationMatcher( filepath, 10, 4 ) + ), + ChunkMatcher( + '\n {\n ', + LocationMatcher( filepath, 7, 42 ), + LocationMatcher( filepath, 9, 4 ) + ), + ChunkMatcher( + '', + LocationMatcher( filepath, 7, 26 ), + LocationMatcher( filepath, 7, 27 ) + ), + ChunkMatcher( + '\n class MainClass\n {\n ', + LocationMatcher( filepath, 4, 2 ), + LocationMatcher( filepath, 7, 3 ) + ), + ) } ) ) } ) ) + else: assert_that( response, has_entries( { 'fixits': contains_exactly( has_entries( { 'location': LocationMatcher( filepath, 1, 1 ), @@ -844,70 +716,65 @@ def test_Subcommands_Format_Works( self, app ): @SharedYcmd def test_Subcommands_RangeFormat_Works( self, app ): filepath = PathToTestFile( 'testy', 'Program.cs' ) - with WrapOmniSharpServer( app, filepath ): - contents = ReadFile( filepath ) + contents = ReadFile( filepath ) - request = BuildRequest( command_arguments = [ 'Format' ], - line_num = 11, - column_num = 2, - contents = contents, - filetype = 'cs', - filepath = filepath ) - request[ 'range' ] = { - 'start': { 'line_num': 8, 'column_num': 1 }, - 'end': { 'line_num': 11, 'column_num': 4 } - } - response = app.post_json( '/run_completer_command', request ).json - print( 'completer response = ', response ) + request = BuildRequest( command_arguments = [ 'Format' ], + line_num = 11, + column_num = 2, + contents = contents, + filetype = 'cs', + filepath = filepath ) + request[ 'range' ] = { + 'start': { 'line_num': 8, 'column_num': 1 }, + 'end': { 'line_num': 11, 'column_num': 4 } + } + request[ 'options' ] = { + 'tab_size': 2, + 'insert_spaces': True + } + response = app.post_json( '/run_completer_command', request ).json + print( 'completer response = ', response ) + if OnWindows(): assert_that( response, has_entries( { 'fixits': contains_exactly( has_entries( { 'location': LocationMatcher( filepath, 11, 2 ), 'chunks': contains_exactly( ChunkMatcher( - '\n ', + '\n }\n ', LocationMatcher( filepath, 11, 1 ), - LocationMatcher( filepath, 11, 3 ) + LocationMatcher( filepath, 12, 2 ) ), ChunkMatcher( - ' ', - LocationMatcher( filepath, 10, 1 ), + '\n ', + LocationMatcher( filepath, 9, 16 ), LocationMatcher( filepath, 10, 4 ) ), ChunkMatcher( - ' {\n ', - LocationMatcher( filepath, 8, 1 ), + '\n {\n ', + LocationMatcher( filepath, 7, 42 ), LocationMatcher( filepath, 9, 4 ) ), ) } ) ) } ) ) - - - @SharedYcmd - def test_Subcommands_OrganizeImports( self, app ): - filepath = PathToTestFile( 'testy', 'ImportTest.cs' ) - with WrapOmniSharpServer( app, filepath ): - request = BuildRequest( command_arguments = [ 'OrganizeImports' ], - line_num = 11, - column_num = 2, - contents = ReadFile( filepath ), - filetype = 'cs', - filepath = filepath ) - - response = app.post_json( '/run_completer_command', request ).json - print( 'completer response = ', response ) + else: assert_that( response, has_entries( { 'fixits': contains_exactly( has_entries( { 'location': LocationMatcher( filepath, 11, 2 ), 'chunks': contains_exactly( ChunkMatcher( - ' ', - LocationMatcher( filepath, 5, 1 ), - LocationMatcher( filepath, 5, 2 ), + '\n }\n ', + LocationMatcher( filepath, 11, 1 ), + LocationMatcher( filepath, 12, 2 ) ), ChunkMatcher( - '', - LocationMatcher( filepath, 1, 1 ), - LocationMatcher( filepath, 3, 1 ), - ) + ' ', + LocationMatcher( filepath, 10, 1 ), + LocationMatcher( filepath, 10, 4 ) + ), + ChunkMatcher( + ' {\n ', + LocationMatcher( filepath, 8, 1 ), + LocationMatcher( filepath, 9, 4 ) + ), ) } ) ) } ) ) @@ -916,61 +783,61 @@ def test_Subcommands_GoToDocumentOutline( self, app ): for filepath, expected in [ ( PathToTestFile( 'testy', 'Empty.cs' ), - ErrorMatcher( RuntimeError, 'No symbols found' ) ), + ErrorMatcher( RuntimeError, 'Symbol not found' ) ), ( PathToTestFile( 'testy', 'SingleEntity.cs' ), LocationMatcher( - PathToTestFile( 'testy', 'SingleEntity.cs' ), 6, 8 ) ), + PathToTestFile( 'testy', 'SingleEntity.cs' ), 4, 1 ) ), ( PathToTestFile( 'testy', 'GotoTestCase.cs' ), has_items( LocationMatcher( - PathToTestFile( 'testy', 'GotoTestCase.cs' ), 6, 8 ), + PathToTestFile( 'testy', 'GotoTestCase.cs' ), 4, 1 ), LocationMatcher( - PathToTestFile( 'testy', 'GotoTestCase.cs' ), 26, 12 ), + PathToTestFile( 'testy', 'GotoTestCase.cs' ), 6, 2 ), LocationMatcher( - PathToTestFile( 'testy', 'GotoTestCase.cs' ), 30, 8 ), + PathToTestFile( 'testy', 'GotoTestCase.cs' ), 30, 2 ), LocationMatcher( - PathToTestFile( 'testy', 'GotoTestCase.cs' ), 35, 12 ), + PathToTestFile( 'testy', 'GotoTestCase.cs' ), 43, 2 ), LocationMatcher( - PathToTestFile( 'testy', 'GotoTestCase.cs' ), 39, 12 ), + PathToTestFile( 'testy', 'GotoTestCase.cs' ), 48, 2 ), LocationMatcher( - PathToTestFile( 'testy', 'GotoTestCase.cs' ), 43, 8 ), + PathToTestFile( 'testy', 'GotoTestCase.cs' ), 27, 3 ), LocationMatcher( - PathToTestFile( 'testy', 'GotoTestCase.cs' ), 48, 8 ), + PathToTestFile( 'testy', 'GotoTestCase.cs' ), 31, 3 ), LocationMatcher( - PathToTestFile( 'testy', 'GotoTestCase.cs' ), 8, 15 ), + PathToTestFile( 'testy', 'GotoTestCase.cs' ), 36, 3 ), LocationMatcher( - PathToTestFile( 'testy', 'GotoTestCase.cs' ), 13, 15 ), + PathToTestFile( 'testy', 'GotoTestCase.cs' ), 40, 3 ), LocationMatcher( - PathToTestFile( 'testy', 'GotoTestCase.cs' ), 17, 15 ), + PathToTestFile( 'testy', 'GotoTestCase.cs' ), 44, 3 ), LocationMatcher( - PathToTestFile( 'testy', 'GotoTestCase.cs' ), 21, 15 ), + PathToTestFile( 'testy', 'GotoTestCase.cs' ), 49, 3 ), LocationMatcher( - PathToTestFile( 'testy', 'GotoTestCase.cs' ), 27, 8 ), + PathToTestFile( 'testy', 'GotoTestCase.cs' ), 13, 3 ), LocationMatcher( - PathToTestFile( 'testy', 'GotoTestCase.cs' ), 31, 15 ), + PathToTestFile( 'testy', 'GotoTestCase.cs' ), 17, 3 ), LocationMatcher( - PathToTestFile( 'testy', 'GotoTestCase.cs' ), 36, 8 ), + PathToTestFile( 'testy', 'GotoTestCase.cs' ), 8, 3 ), LocationMatcher( - PathToTestFile( 'testy', 'GotoTestCase.cs' ), 40, 8 ), + PathToTestFile( 'testy', 'GotoTestCase.cs' ), 21, 3 ), LocationMatcher( - PathToTestFile( 'testy', 'GotoTestCase.cs' ), 44, 15 ), + PathToTestFile( 'testy', 'GotoTestCase.cs' ), 26, 2 ), LocationMatcher( - PathToTestFile( 'testy', 'GotoTestCase.cs' ), 49, 15 ) ) ) + PathToTestFile( 'testy', 'GotoTestCase.cs' ), 39, 2 ), + LocationMatcher( + PathToTestFile( 'testy', 'GotoTestCase.cs' ), 35, 2 ) ) ) ]: with self.subTest( filepath = filepath, expected = expected ): - with WrapOmniSharpServer( app, filepath ): - - # the command name and file are the only relevant arguments for this - # subcommand. our current cursor position in the file doesn't matter. - request = BuildRequest( command_arguments = [ 'GoToDocumentOutline' ], - line_num = 0, - column_num = 0, - contents = ReadFile( filepath ), - filetype = 'cs', - filepath = filepath ) + # the command name and file are the only relevant arguments for this + # subcommand. our current cursor position in the file doesn't matter. + request = BuildRequest( command_arguments = [ 'GoToDocumentOutline' ], + line_num = 0, + column_num = 0, + contents = ReadFile( filepath ), + filetype = 'cs', + filepath = filepath ) - response = app.post_json( '/run_completer_command', - request, - expect_errors = True ).json - print( 'completer response = ', response ) - assert_that( response, expected ) + response = app.post_json( '/run_completer_command', + request, + expect_errors = True ).json + print( 'completer response = ', response ) + assert_that( response, expected ) diff --git a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder/not-testy/Program.cs b/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder/not-testy/Program.cs deleted file mode 100644 index cac606c88f..0000000000 --- a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder/not-testy/Program.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace testy -{ - class MainClass - { - public static void Main (string[] args) - { - Console. - } - } -} diff --git a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder/not-testy/Properties/AssemblyInfo.cs b/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder/not-testy/Properties/AssemblyInfo.cs deleted file mode 100644 index 2121d05e99..0000000000 --- a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder/not-testy/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; - -// Information about this assembly is defined by the following attributes. -// Change them to the values specific to your project. -[assembly: AssemblyTitle ("testy")] -[assembly: AssemblyDescription ("")] -[assembly: AssemblyConfiguration ("")] -[assembly: AssemblyCompany ("")] -[assembly: AssemblyProduct ("")] -[assembly: AssemblyCopyright ("valloric")] -[assembly: AssemblyTrademark ("")] -[assembly: AssemblyCulture ("")] -// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". -// The form "{Major}.{Minor}.*" will automatically update the build and revision, -// and "{Major}.{Minor}.{Build}.*" will update just the revision. -[assembly: AssemblyVersion ("1.0.*")] -// The following attributes are used to specify the signing key for the assembly, -// if desired. See the Mono documentation for more information about signing. -//[assembly: AssemblyDelaySign(false)] -//[assembly: AssemblyKeyFile("")] - diff --git a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder/not-testy/testy.csproj b/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder/not-testy/testy.csproj deleted file mode 100644 index e8b5f0cffc..0000000000 --- a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder/not-testy/testy.csproj +++ /dev/null @@ -1,43 +0,0 @@ - - - - Debug - x86 - 10.0.0 - 2.0 - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F} - Exe - testy - testy - v4.8 - - - - true - full - false - bin\Debug - DEBUG; - prompt - 4 - true - x86 - - - full - true - bin\Release - prompt - 4 - true - x86 - - - - - - - - - - diff --git a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder/solution-named-like-folder.sln b/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder/solution-named-like-folder.sln deleted file mode 100644 index 4fb02619aa..0000000000 --- a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder/solution-named-like-folder.sln +++ /dev/null @@ -1,20 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "testy", "testy/testy.csproj", "{0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x86 = Debug|x86 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Debug|x86.ActiveCfg = Debug|x86 - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Debug|x86.Build.0 = Debug|x86 - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Release|x86.ActiveCfg = Release|x86 - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Release|x86.Build.0 = Release|x86 - EndGlobalSection - GlobalSection(MonoDevelopProperties) = preSolution - StartupItem = not-testy/testy.csproj - EndGlobalSection -EndGlobal diff --git a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder/testy.sln b/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder/testy.sln deleted file mode 100644 index 82e3d1557a..0000000000 --- a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder/testy.sln +++ /dev/null @@ -1,20 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "testy", "testy/testy.csproj", "{0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x86 = Debug|x86 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Debug|x86.ActiveCfg = Debug|x86 - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Debug|x86.Build.0 = Debug|x86 - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Release|x86.ActiveCfg = Release|x86 - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Release|x86.Build.0 = Release|x86 - EndGlobalSection - GlobalSection(MonoDevelopProperties) = preSolution - StartupItem = testy/testy.csproj - EndGlobalSection -EndGlobal diff --git a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder/testy/Program.cs b/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder/testy/Program.cs deleted file mode 100644 index b4d0073db1..0000000000 --- a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder/testy/Program.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; - -namespace testy -{ - class MainClass - { - public static void Main (string[] args) - { - int 九 = 9; - Console. - } - } -} diff --git a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder/testy/Properties/AssemblyInfo.cs b/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder/testy/Properties/AssemblyInfo.cs deleted file mode 100644 index 2121d05e99..0000000000 --- a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder/testy/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; - -// Information about this assembly is defined by the following attributes. -// Change them to the values specific to your project. -[assembly: AssemblyTitle ("testy")] -[assembly: AssemblyDescription ("")] -[assembly: AssemblyConfiguration ("")] -[assembly: AssemblyCompany ("")] -[assembly: AssemblyProduct ("")] -[assembly: AssemblyCopyright ("valloric")] -[assembly: AssemblyTrademark ("")] -[assembly: AssemblyCulture ("")] -// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". -// The form "{Major}.{Minor}.*" will automatically update the build and revision, -// and "{Major}.{Minor}.{Build}.*" will update just the revision. -[assembly: AssemblyVersion ("1.0.*")] -// The following attributes are used to specify the signing key for the assembly, -// if desired. See the Mono documentation for more information about signing. -//[assembly: AssemblyDelaySign(false)] -//[assembly: AssemblyKeyFile("")] - diff --git a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder/testy/testy.csproj b/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder/testy/testy.csproj deleted file mode 100644 index e8b5f0cffc..0000000000 --- a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder/testy/testy.csproj +++ /dev/null @@ -1,43 +0,0 @@ - - - - Debug - x86 - 10.0.0 - 2.0 - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F} - Exe - testy - testy - v4.8 - - - - true - full - false - bin\Debug - DEBUG; - prompt - 4 - true - x86 - - - full - true - bin\Release - prompt - 4 - true - x86 - - - - - - - - - - diff --git a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder/testy2.sln b/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder/testy2.sln deleted file mode 100644 index 82e3d1557a..0000000000 --- a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder/testy2.sln +++ /dev/null @@ -1,20 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "testy", "testy/testy.csproj", "{0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x86 = Debug|x86 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Debug|x86.ActiveCfg = Debug|x86 - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Debug|x86.Build.0 = Debug|x86 - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Release|x86.ActiveCfg = Release|x86 - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Release|x86.Build.0 = Release|x86 - EndGlobalSection - GlobalSection(MonoDevelopProperties) = preSolution - StartupItem = testy/testy.csproj - EndGlobalSection -EndGlobal diff --git a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-abs/.ycm_extra_conf.py b/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-abs/.ycm_extra_conf.py deleted file mode 100644 index 54261e6531..0000000000 --- a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-abs/.ycm_extra_conf.py +++ /dev/null @@ -1,9 +0,0 @@ - -import os - -def CSharpSolutionFile( path, **kwargs ): - #remove '.ycm_extra_conf.py' and 'extra-conf-abs' from filename - location=os.path.dirname( __file__ ) - location=os.path.dirname( location ) - return os.path.join( location, 'testy2.sln' ) - diff --git a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-abs/testy/Program.cs b/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-abs/testy/Program.cs deleted file mode 100644 index cac606c88f..0000000000 --- a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-abs/testy/Program.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace testy -{ - class MainClass - { - public static void Main (string[] args) - { - Console. - } - } -} diff --git a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-abs/testy/Properties/AssemblyInfo.cs b/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-abs/testy/Properties/AssemblyInfo.cs deleted file mode 100644 index 2121d05e99..0000000000 --- a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-abs/testy/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; - -// Information about this assembly is defined by the following attributes. -// Change them to the values specific to your project. -[assembly: AssemblyTitle ("testy")] -[assembly: AssemblyDescription ("")] -[assembly: AssemblyConfiguration ("")] -[assembly: AssemblyCompany ("")] -[assembly: AssemblyProduct ("")] -[assembly: AssemblyCopyright ("valloric")] -[assembly: AssemblyTrademark ("")] -[assembly: AssemblyCulture ("")] -// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". -// The form "{Major}.{Minor}.*" will automatically update the build and revision, -// and "{Major}.{Minor}.{Build}.*" will update just the revision. -[assembly: AssemblyVersion ("1.0.*")] -// The following attributes are used to specify the signing key for the assembly, -// if desired. See the Mono documentation for more information about signing. -//[assembly: AssemblyDelaySign(false)] -//[assembly: AssemblyKeyFile("")] - diff --git a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-abs/testy/testy.csproj b/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-abs/testy/testy.csproj deleted file mode 100644 index e8b5f0cffc..0000000000 --- a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-abs/testy/testy.csproj +++ /dev/null @@ -1,43 +0,0 @@ - - - - Debug - x86 - 10.0.0 - 2.0 - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F} - Exe - testy - testy - v4.8 - - - - true - full - false - bin\Debug - DEBUG; - prompt - 4 - true - x86 - - - full - true - bin\Release - prompt - 4 - true - x86 - - - - - - - - - - diff --git a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-abs/testy2.sln b/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-abs/testy2.sln deleted file mode 100644 index 82e3d1557a..0000000000 --- a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-abs/testy2.sln +++ /dev/null @@ -1,20 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "testy", "testy/testy.csproj", "{0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x86 = Debug|x86 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Debug|x86.ActiveCfg = Debug|x86 - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Debug|x86.Build.0 = Debug|x86 - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Release|x86.ActiveCfg = Release|x86 - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Release|x86.Build.0 = Release|x86 - EndGlobalSection - GlobalSection(MonoDevelopProperties) = preSolution - StartupItem = testy/testy.csproj - EndGlobalSection -EndGlobal diff --git a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-bad/testy/.ycm_extra_conf.py b/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-bad/testy/.ycm_extra_conf.py deleted file mode 100644 index 22c2901ac7..0000000000 --- a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-bad/testy/.ycm_extra_conf.py +++ /dev/null @@ -1,4 +0,0 @@ - -def CSharpSolutionFile( path, **kwargs ): - return "nosuch.sln" - diff --git a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-bad/testy/Program.cs b/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-bad/testy/Program.cs deleted file mode 100644 index cac606c88f..0000000000 --- a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-bad/testy/Program.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace testy -{ - class MainClass - { - public static void Main (string[] args) - { - Console. - } - } -} diff --git a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-bad/testy/Properties/AssemblyInfo.cs b/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-bad/testy/Properties/AssemblyInfo.cs deleted file mode 100644 index 2121d05e99..0000000000 --- a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-bad/testy/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; - -// Information about this assembly is defined by the following attributes. -// Change them to the values specific to your project. -[assembly: AssemblyTitle ("testy")] -[assembly: AssemblyDescription ("")] -[assembly: AssemblyConfiguration ("")] -[assembly: AssemblyCompany ("")] -[assembly: AssemblyProduct ("")] -[assembly: AssemblyCopyright ("valloric")] -[assembly: AssemblyTrademark ("")] -[assembly: AssemblyCulture ("")] -// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". -// The form "{Major}.{Minor}.*" will automatically update the build and revision, -// and "{Major}.{Minor}.{Build}.*" will update just the revision. -[assembly: AssemblyVersion ("1.0.*")] -// The following attributes are used to specify the signing key for the assembly, -// if desired. See the Mono documentation for more information about signing. -//[assembly: AssemblyDelaySign(false)] -//[assembly: AssemblyKeyFile("")] - diff --git a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-bad/testy/testy.csproj b/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-bad/testy/testy.csproj deleted file mode 100644 index e8b5f0cffc..0000000000 --- a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-bad/testy/testy.csproj +++ /dev/null @@ -1,43 +0,0 @@ - - - - Debug - x86 - 10.0.0 - 2.0 - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F} - Exe - testy - testy - v4.8 - - - - true - full - false - bin\Debug - DEBUG; - prompt - 4 - true - x86 - - - full - true - bin\Release - prompt - 4 - true - x86 - - - - - - - - - - diff --git a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-bad/testy2.sln b/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-bad/testy2.sln deleted file mode 100644 index 82e3d1557a..0000000000 --- a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-bad/testy2.sln +++ /dev/null @@ -1,20 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "testy", "testy/testy.csproj", "{0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x86 = Debug|x86 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Debug|x86.ActiveCfg = Debug|x86 - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Debug|x86.Build.0 = Debug|x86 - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Release|x86.ActiveCfg = Release|x86 - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Release|x86.Build.0 = Release|x86 - EndGlobalSection - GlobalSection(MonoDevelopProperties) = preSolution - StartupItem = testy/testy.csproj - EndGlobalSection -EndGlobal diff --git a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-rel/.ycm_extra_conf.py b/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-rel/.ycm_extra_conf.py deleted file mode 100644 index aaa3193ec5..0000000000 --- a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-rel/.ycm_extra_conf.py +++ /dev/null @@ -1,4 +0,0 @@ - -def CSharpSolutionFile( path, **kwargs ): - return "testy2.sln" - diff --git a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-rel/testy/Program.cs b/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-rel/testy/Program.cs deleted file mode 100644 index cac606c88f..0000000000 --- a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-rel/testy/Program.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace testy -{ - class MainClass - { - public static void Main (string[] args) - { - Console. - } - } -} diff --git a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-rel/testy/Properties/AssemblyInfo.cs b/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-rel/testy/Properties/AssemblyInfo.cs deleted file mode 100644 index 2121d05e99..0000000000 --- a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-rel/testy/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; - -// Information about this assembly is defined by the following attributes. -// Change them to the values specific to your project. -[assembly: AssemblyTitle ("testy")] -[assembly: AssemblyDescription ("")] -[assembly: AssemblyConfiguration ("")] -[assembly: AssemblyCompany ("")] -[assembly: AssemblyProduct ("")] -[assembly: AssemblyCopyright ("valloric")] -[assembly: AssemblyTrademark ("")] -[assembly: AssemblyCulture ("")] -// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". -// The form "{Major}.{Minor}.*" will automatically update the build and revision, -// and "{Major}.{Minor}.{Build}.*" will update just the revision. -[assembly: AssemblyVersion ("1.0.*")] -// The following attributes are used to specify the signing key for the assembly, -// if desired. See the Mono documentation for more information about signing. -//[assembly: AssemblyDelaySign(false)] -//[assembly: AssemblyKeyFile("")] - diff --git a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-rel/testy/testy.csproj b/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-rel/testy/testy.csproj deleted file mode 100644 index e8b5f0cffc..0000000000 --- a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-rel/testy/testy.csproj +++ /dev/null @@ -1,43 +0,0 @@ - - - - Debug - x86 - 10.0.0 - 2.0 - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F} - Exe - testy - testy - v4.8 - - - - true - full - false - bin\Debug - DEBUG; - prompt - 4 - true - x86 - - - full - true - bin\Release - prompt - 4 - true - x86 - - - - - - - - - - diff --git a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-rel/testy/testy2.sln b/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-rel/testy/testy2.sln deleted file mode 100644 index fff2c0f590..0000000000 --- a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-rel/testy/testy2.sln +++ /dev/null @@ -1,20 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "testy", "testy.csproj", "{0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x86 = Debug|x86 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Debug|x86.ActiveCfg = Debug|x86 - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Debug|x86.Build.0 = Debug|x86 - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Release|x86.ActiveCfg = Release|x86 - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Release|x86.Build.0 = Release|x86 - EndGlobalSection - GlobalSection(MonoDevelopProperties) = preSolution - StartupItem = testy/testy.csproj - EndGlobalSection -EndGlobal diff --git a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-rel/testy2.sln b/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-rel/testy2.sln deleted file mode 100644 index 82e3d1557a..0000000000 --- a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-rel/testy2.sln +++ /dev/null @@ -1,20 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "testy", "testy/testy.csproj", "{0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x86 = Debug|x86 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Debug|x86.ActiveCfg = Debug|x86 - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Debug|x86.Build.0 = Debug|x86 - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Release|x86.ActiveCfg = Release|x86 - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Release|x86.Build.0 = Release|x86 - EndGlobalSection - GlobalSection(MonoDevelopProperties) = preSolution - StartupItem = testy/testy.csproj - EndGlobalSection -EndGlobal diff --git a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/testy/Program.cs b/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/testy/Program.cs deleted file mode 100644 index cac606c88f..0000000000 --- a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/testy/Program.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace testy -{ - class MainClass - { - public static void Main (string[] args) - { - Console. - } - } -} diff --git a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/testy/Properties/AssemblyInfo.cs b/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/testy/Properties/AssemblyInfo.cs deleted file mode 100644 index 2121d05e99..0000000000 --- a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/testy/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; - -// Information about this assembly is defined by the following attributes. -// Change them to the values specific to your project. -[assembly: AssemblyTitle ("testy")] -[assembly: AssemblyDescription ("")] -[assembly: AssemblyConfiguration ("")] -[assembly: AssemblyCompany ("")] -[assembly: AssemblyProduct ("")] -[assembly: AssemblyCopyright ("valloric")] -[assembly: AssemblyTrademark ("")] -[assembly: AssemblyCulture ("")] -// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". -// The form "{Major}.{Minor}.*" will automatically update the build and revision, -// and "{Major}.{Minor}.{Build}.*" will update just the revision. -[assembly: AssemblyVersion ("1.0.*")] -// The following attributes are used to specify the signing key for the assembly, -// if desired. See the Mono documentation for more information about signing. -//[assembly: AssemblyDelaySign(false)] -//[assembly: AssemblyKeyFile("")] - diff --git a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/testy/testy.csproj b/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/testy/testy.csproj deleted file mode 100644 index e8b5f0cffc..0000000000 --- a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/testy/testy.csproj +++ /dev/null @@ -1,43 +0,0 @@ - - - - Debug - x86 - 10.0.0 - 2.0 - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F} - Exe - testy - testy - v4.8 - - - - true - full - false - bin\Debug - DEBUG; - prompt - 4 - true - x86 - - - full - true - bin\Release - prompt - 4 - true - x86 - - - - - - - - - - diff --git a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/testy1.sln b/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/testy1.sln deleted file mode 100644 index 82e3d1557a..0000000000 --- a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/testy1.sln +++ /dev/null @@ -1,20 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "testy", "testy/testy.csproj", "{0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x86 = Debug|x86 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Debug|x86.ActiveCfg = Debug|x86 - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Debug|x86.Build.0 = Debug|x86 - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Release|x86.ActiveCfg = Release|x86 - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Release|x86.Build.0 = Release|x86 - EndGlobalSection - GlobalSection(MonoDevelopProperties) = preSolution - StartupItem = testy/testy.csproj - EndGlobalSection -EndGlobal diff --git a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/testy2.sln b/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/testy2.sln deleted file mode 100644 index 82e3d1557a..0000000000 --- a/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/testy2.sln +++ /dev/null @@ -1,20 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "testy", "testy/testy.csproj", "{0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x86 = Debug|x86 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Debug|x86.ActiveCfg = Debug|x86 - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Debug|x86.Build.0 = Debug|x86 - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Release|x86.ActiveCfg = Release|x86 - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Release|x86.Build.0 = Release|x86 - EndGlobalSection - GlobalSection(MonoDevelopProperties) = preSolution - StartupItem = testy/testy.csproj - EndGlobalSection -EndGlobal diff --git a/ycmd/tests/cs/testdata/testy/DiagnosticRange.cs b/ycmd/tests/cs/testdata/testy/DiagnosticRange.cs deleted file mode 100644 index 2fc22e2bd5..0000000000 --- a/ycmd/tests/cs/testdata/testy/DiagnosticRange.cs +++ /dev/null @@ -1,8 +0,0 @@ -public class Class -{ - public int attribute; - public static void Main (string[] args) - { - int 九 = 9; - } -} diff --git a/ycmd/tests/cs/testdata/testy/Empty.cs b/ycmd/tests/cs/testdata/testy/Empty.cs index fbdadd554e..1372ec54f6 100644 --- a/ycmd/tests/cs/testdata/testy/Empty.cs +++ b/ycmd/tests/cs/testdata/testy/Empty.cs @@ -1,6 +1,2 @@ using System; using System.Data; - -namespace testy -{ -} diff --git a/ycmd/tests/cs/testdata/testy/MaxDiagnostics.cs b/ycmd/tests/cs/testdata/testy/MaxDiagnostics.cs deleted file mode 100644 index ed5a2174c0..0000000000 --- a/ycmd/tests/cs/testdata/testy/MaxDiagnostics.cs +++ /dev/null @@ -1,6 +0,0 @@ -public class MaxDiagnostics -{ - public int test; - public int test; - public int test; -} diff --git a/ycmd/tests/cs/testdata/testy/SingleEntity.cs b/ycmd/tests/cs/testdata/testy/SingleEntity.cs index 594f1a8fea..fbdadd554e 100644 --- a/ycmd/tests/cs/testdata/testy/SingleEntity.cs +++ b/ycmd/tests/cs/testdata/testy/SingleEntity.cs @@ -3,7 +3,4 @@ namespace testy { - class GotoTestCase - { - } } diff --git a/ycmd/tests/cs/testdata/testy/ZeroColumnDiagnostic.cs b/ycmd/tests/cs/testdata/testy/ZeroColumnDiagnostic.cs deleted file mode 100644 index 7e08027751..0000000000 --- a/ycmd/tests/cs/testdata/testy/ZeroColumnDiagnostic.cs +++ /dev/null @@ -1,3 +0,0 @@ -class Class{ - int Method() -} diff --git a/ycmd/tests/cs/testdata/testy/testy.csproj b/ycmd/tests/cs/testdata/testy/testy.csproj index 6e2787b5ba..bac5eca164 100644 --- a/ycmd/tests/cs/testdata/testy/testy.csproj +++ b/ycmd/tests/cs/testdata/testy/testy.csproj @@ -1,55 +1,6 @@ - - + - Debug - x86 - 10.0.0 - 2.0 - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F} Exe - testy - testy - v4.8 - + net8.0 - - true - full - false - bin\Debug - DEBUG; - prompt - 4 - true - x86 - - - full - true - bin\Release - prompt - 4 - true - x86 - - - - - - - - - - - - - - - - - - - - - diff --git "a/ycmd/tests/cs/testdata/\320\275\320\265\320\277\321\200\320\270\320\273\320\270\321\207\320\275\320\276\320\265 \321\201\320\273\320\276\320\262\320\276/Program.cs" "b/ycmd/tests/cs/testdata/\320\275\320\265\320\277\321\200\320\270\320\273\320\270\321\207\320\275\320\276\320\265 \321\201\320\273\320\276\320\262\320\276/Program.cs" deleted file mode 100644 index cac606c88f..0000000000 --- "a/ycmd/tests/cs/testdata/\320\275\320\265\320\277\321\200\320\270\320\273\320\270\321\207\320\275\320\276\320\265 \321\201\320\273\320\276\320\262\320\276/Program.cs" +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace testy -{ - class MainClass - { - public static void Main (string[] args) - { - Console. - } - } -} diff --git "a/ycmd/tests/cs/testdata/\320\275\320\265\320\277\321\200\320\270\320\273\320\270\321\207\320\275\320\276\320\265 \321\201\320\273\320\276\320\262\320\276/Properties/AssemblyInfo.cs" "b/ycmd/tests/cs/testdata/\320\275\320\265\320\277\321\200\320\270\320\273\320\270\321\207\320\275\320\276\320\265 \321\201\320\273\320\276\320\262\320\276/Properties/AssemblyInfo.cs" deleted file mode 100644 index 2121d05e99..0000000000 --- "a/ycmd/tests/cs/testdata/\320\275\320\265\320\277\321\200\320\270\320\273\320\270\321\207\320\275\320\276\320\265 \321\201\320\273\320\276\320\262\320\276/Properties/AssemblyInfo.cs" +++ /dev/null @@ -1,22 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; - -// Information about this assembly is defined by the following attributes. -// Change them to the values specific to your project. -[assembly: AssemblyTitle ("testy")] -[assembly: AssemblyDescription ("")] -[assembly: AssemblyConfiguration ("")] -[assembly: AssemblyCompany ("")] -[assembly: AssemblyProduct ("")] -[assembly: AssemblyCopyright ("valloric")] -[assembly: AssemblyTrademark ("")] -[assembly: AssemblyCulture ("")] -// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". -// The form "{Major}.{Minor}.*" will automatically update the build and revision, -// and "{Major}.{Minor}.{Build}.*" will update just the revision. -[assembly: AssemblyVersion ("1.0.*")] -// The following attributes are used to specify the signing key for the assembly, -// if desired. See the Mono documentation for more information about signing. -//[assembly: AssemblyDelaySign(false)] -//[assembly: AssemblyKeyFile("")] - diff --git "a/ycmd/tests/cs/testdata/\320\275\320\265\320\277\321\200\320\270\320\273\320\270\321\207\320\275\320\276\320\265 \321\201\320\273\320\276\320\262\320\276/a project.csproj" "b/ycmd/tests/cs/testdata/\320\275\320\265\320\277\321\200\320\270\320\273\320\270\321\207\320\275\320\276\320\265 \321\201\320\273\320\276\320\262\320\276/a project.csproj" deleted file mode 100644 index 800f5a87a7..0000000000 --- "a/ycmd/tests/cs/testdata/\320\275\320\265\320\277\321\200\320\270\320\273\320\270\321\207\320\275\320\276\320\265 \321\201\320\273\320\276\320\262\320\276/a project.csproj" +++ /dev/null @@ -1,43 +0,0 @@ - - - - Debug - x86 - 10.0.0 - 2.0 - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F} - Exe - testy - testy - v4.8 - - - - true - full - false - bin\Debug - DEBUG; - prompt - 4 - true - x86 - - - full - true - bin\Release - prompt - 4 - true - x86 - - - - - - - - - - diff --git "a/ycmd/tests/cs/testdata/\320\275\320\265\320\277\321\200\320\270\320\273\320\270\321\207\320\275\320\276\320\265 \321\201\320\273\320\276\320\262\320\276/a project.sln" "b/ycmd/tests/cs/testdata/\320\275\320\265\320\277\321\200\320\270\320\273\320\270\321\207\320\275\320\276\320\265 \321\201\320\273\320\276\320\262\320\276/a project.sln" deleted file mode 100644 index fc10298766..0000000000 --- "a/ycmd/tests/cs/testdata/\320\275\320\265\320\277\321\200\320\270\320\273\320\270\321\207\320\275\320\276\320\265 \321\201\320\273\320\276\320\262\320\276/a project.sln" +++ /dev/null @@ -1,20 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "a project", "a project.csproj", "{0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x86 = Debug|x86 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Debug|x86.ActiveCfg = Debug|x86 - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Debug|x86.Build.0 = Debug|x86 - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Release|x86.ActiveCfg = Release|x86 - {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Release|x86.Build.0 = Release|x86 - EndGlobalSection - GlobalSection(MonoDevelopProperties) = preSolution - StartupItem = a project.csproj - EndGlobalSection -EndGlobal diff --git "a/ycmd/tests/cs/testdata/\320\275\320\265\320\277\321\200\320\270\320\273\320\270\321\207\320\275\320\276\320\265 \321\201\320\273\320\276\320\262\320\276/a project.userprefs" "b/ycmd/tests/cs/testdata/\320\275\320\265\320\277\321\200\320\270\320\273\320\270\321\207\320\275\320\276\320\265 \321\201\320\273\320\276\320\262\320\276/a project.userprefs" deleted file mode 100644 index 1895a66a4d..0000000000 --- "a/ycmd/tests/cs/testdata/\320\275\320\265\320\277\321\200\320\270\320\273\320\270\321\207\320\275\320\276\320\265 \321\201\320\273\320\276\320\262\320\276/a project.userprefs" +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file From 9ac9743cbf0c56b254b84b699b780b51354cc3f9 Mon Sep 17 00:00:00 2001 From: Boris Staletic Date: Tue, 13 Aug 2024 08:08:02 +0200 Subject: [PATCH 4/7] Add support for GetDoc in C# LSP OmniSharp-Roslyn uses a different request than we previously have for this purposes. This results in some questionable formatting, but there's no way we can fix that without breaking other stuff. --- ycmd/completers/cs/cs_completer.py | 29 ++++++-- ycmd/tests/cs/subcommands_test.py | 67 ++++++++++++++++++- .../tests/cs/testdata/testy/GetDocTestCase.cs | 36 +++++----- 3 files changed, 108 insertions(+), 24 deletions(-) diff --git a/ycmd/completers/cs/cs_completer.py b/ycmd/completers/cs/cs_completer.py index dbf44351f4..eb0b74ba3b 100644 --- a/ycmd/completers/cs/cs_completer.py +++ b/ycmd/completers/cs/cs_completer.py @@ -99,9 +99,28 @@ def SupportedFiletypes( self ): def GetType( self, request_data ): - raw_hover = self.GetHoverResponse( request_data ) - value = raw_hover[ 'value' ] - if not value: + hover_value = self.GetHoverResponse( request_data )[ 'value' ] + if not hover_value: raise RuntimeError( 'No type found.' ) - value = value.split( '\n' )[ 1 ] - return responses.BuildDetailedInfoResponse( value ) + value = hover_value.split( '\n', maxsplit = 2 )[ 1 ] + return responses.BuildDisplayMessageResponse( value ) + + + def GetDoc( self, request_data ): + hover_value = self.GetHoverResponse( request_data )[ 'value' ] + if not hover_value: + raise RuntimeError( 'No documentation available.' ) + # The response looks like this: + # + # ```csharp + # type info + # ``` + # + # docstring + # + # The idea is to get rid of silly markdown backticks. + lines = hover_value.splitlines() + del lines[ 2 ] + del lines[ 0 ] + result = '\n'.join( lines ) + return responses.BuildDetailedInfoResponse( result ) diff --git a/ycmd/tests/cs/subcommands_test.py b/ycmd/tests/cs/subcommands_test.py index d06511c6b7..01db587a32 100644 --- a/ycmd/tests/cs/subcommands_test.py +++ b/ycmd/tests/cs/subcommands_test.py @@ -499,7 +499,7 @@ def test_Subcommands_GetType_VariableDeclaration( self, app ): filepath = filepath ) response = app.post_json( '/run_completer_command', gettype_data ).json - assert_that( response, has_entry( 'detailed_info', + assert_that( response, has_entry( 'message', '(local variable) string str' ) ) @@ -517,7 +517,7 @@ def test_Subcommands_GetType_VariableUsage( self, app ): filepath = filepath ) response = app.post_json( '/run_completer_command', gettype_data ).json - assert_that( response, has_entry( 'detailed_info', + assert_that( response, has_entry( 'message', '(local variable) string str' ) ) @@ -536,7 +536,68 @@ def test_Subcommands_GetType_DocsIgnored( self, app ): response = app.post_json( '/run_completer_command', gettype_data ).json assert_that( response, has_entry( - 'detailed_info', '(field) int GetTypeTestCase.an_int_with_docs' ) ) + 'message', '(field) int GetTypeTestCase.an_int_with_docs' ) ) + + + @SharedYcmd + def test_Subcommands_GetDoc_Invalid( self, app ): + filepath = PathToTestFile( 'testy', 'GetDocTestCase.cs' ) + contents = ReadFile( filepath ) + + getdoc_data = BuildRequest( completer_target = 'filetype_default', + command_arguments = [ 'GetDoc' ], + line_num = 1, + column_num = 1, + contents = contents, + filetype = 'cs', + filepath = filepath ) + + response = app.post_json( '/run_completer_command', + getdoc_data, + expect_errors = True ).json + assert_that( response, ErrorMatcher( RuntimeError, + 'No documentation available.' ) ) + + + @SharedYcmd + def test_Subcommands_GetDoc_Variable( self, app ): + filepath = PathToTestFile( 'testy', 'GetDocTestCase.cs' ) + contents = ReadFile( filepath ) + + getdoc_data = BuildRequest( completer_target = 'filetype_default', + command_arguments = [ 'GetDoc' ], + line_num = 13, + column_num = 28, + contents = contents, + filetype = 'cs', + filepath = filepath ) + + response = app.post_json( '/run_completer_command', getdoc_data ).json + assert_that( response, + has_entry( 'detailed_info', + '(field) int GetDocTestCase.an_int\n\n' + 'an integer, or something' ) ) + + + @SharedYcmd + def test_Subcommands_GetDoc_Function( self, app ): + filepath = PathToTestFile( 'testy', 'GetDocTestCase.cs' ) + contents = ReadFile( filepath ) + + getdoc_data = BuildRequest( completer_target = 'filetype_default', + command_arguments = [ 'GetDoc' ], + line_num = 37, + column_num = 27, + contents = contents, + filetype = 'cs', + filepath = filepath ) + + response = app.post_json( '/run_completer_command', getdoc_data ).json + # And here LSP OmniSharp-Roslyn messes up the formatting. + assert_that( response, has_entry( 'detailed_info', + 'int GetDocTestCase.DoATest()\n\n' + 'Very important method\\. With multiple lines of ' + 'commentary And Format\\- \\-ting' ) ) @IsolatedYcmd() diff --git a/ycmd/tests/cs/testdata/testy/GetDocTestCase.cs b/ycmd/tests/cs/testdata/testy/GetDocTestCase.cs index e54ac61ad9..76a1471fe7 100644 --- a/ycmd/tests/cs/testdata/testy/GetDocTestCase.cs +++ b/ycmd/tests/cs/testdata/testy/GetDocTestCase.cs @@ -1,33 +1,37 @@ -/** - * testy is a namespace for testing - */ +/// +/// testy is a namespace for testing +/// namespace testy { - /** - * Tests the GetDoc subcommands - */ + /// + /// Tests the GetDoc subcommands + /// public class GetDocTestCase { - /** - * Constructor - */ + /// + /// Constructor + /// public GetDocTestCase() { this.an_int = 1; } - /** - * Very important method. - * - * With multiple lines of commentary - * And Format- - * -ting - */ + /// + /// Very important method. + /// + /// With multiple lines of commentary + /// And Format- + /// -ting + /// public int DoATest() { return an_int; } + /// /// an integer, or something + /// private int an_int; + /// /// Use this for testing + /// private static void DoTesting() { GetDocTestCase tc; tc.DoATest(); From c7c069207f9e565d530462122b6ea3dcb20cff02 Mon Sep 17 00:00:00 2001 From: Boris Staletic Date: Sun, 18 Aug 2024 14:16:37 +0200 Subject: [PATCH 5/7] Add tests for omnisharp server management --- ycmd/tests/cs/cs_completer.py | 107 ++++++++++++++++++ ycmd/tests/cs/server_management_test.py | 138 ++++++++++++++++++++++++ ycmd/tests/cs/subcommands_test.py | 58 +++++++++- 3 files changed, 302 insertions(+), 1 deletion(-) create mode 100644 ycmd/tests/cs/cs_completer.py create mode 100644 ycmd/tests/cs/server_management_test.py diff --git a/ycmd/tests/cs/cs_completer.py b/ycmd/tests/cs/cs_completer.py new file mode 100644 index 0000000000..88313f2f5e --- /dev/null +++ b/ycmd/tests/cs/cs_completer.py @@ -0,0 +1,107 @@ +# Copyright (C) 2021 ycmd contributors +# +# This file is part of ycmd. +# +# ycmd 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. +# +# ycmd 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 ycmd. If not, see . + +from unittest.mock import patch +from unittest import TestCase +from hamcrest import assert_that, equal_to + +from ycmd import user_options_store +from ycmd.completers.cs.hook import GetCompleter +from ycmd.completers.cs.cs_completer import PATH_TO_OMNISHARP_ROSLYN_BINARY +from ycmd.tests.cs import setUpModule, tearDownModule # noqa + + +class GoCompleterTest( TestCase ): + def test_GetCompleter_OmniSharpFound( self ): + assert_that( GetCompleter( user_options_store.GetAll() ) ) + + + @patch( 'ycmd.completers.cs.cs_completer.PATH_TO_OMNISHARP_ROSLYN_BINARY', + None ) + def test_GetCompleter_OmniSharpNotFound( self, *args ): + assert_that( not GetCompleter( user_options_store.GetAll() ) ) + + + @patch( 'ycmd.completers.cs.cs_completer.FindExecutableWithFallback', + wraps = lambda x, fb: x if x == 'omnisharp' else None ) + @patch( 'os.path.isfile', return_value = False ) + def test_GetCompleter_CustomOmniSharpNotFound( self, *args ): + user_options = user_options_store.GetAll().copy( + roslyn_binary_path = 'omnisharp' ) + assert_that( not GetCompleter( user_options ) ) + + + @patch( 'ycmd.completers.cs.cs_completer.FindExecutableWithFallback', + wraps = lambda x, fb: x if x == 'omnisharp' else None ) + @patch( 'os.path.isfile', return_value = True ) + def test_GetCompleter_CustomOmniSharpFound_MonoNotRequired( self, *args ): + user_options = user_options_store.GetAll().copy( + roslyn_binary_path = 'omnisharp' ) + assert_that( GetCompleter( user_options ) ) + + + @patch( 'ycmd.completers.cs.cs_completer.FindExecutableWithFallback', + wraps = lambda x, fb: x if x in ( 'mono', + 'omnisharp.exe' ) else None ) + @patch( 'os.path.isfile', return_value = True ) + def test_GetCompleter_CustomOmniSharpFound_MonoRequiredAndFound( self, + *args ): + user_options = user_options_store.GetAll().copy( + roslyn_binary_path = 'omnisharp.exe', + mono_binary_path = 'mono' ) + assert_that( GetCompleter( user_options ) ) + + + @patch( 'ycmd.completers.cs.cs_completer.FindExecutableWithFallback', + wraps = lambda x, fb: x if x == 'omnisharp.exe' else None ) + @patch( 'os.path.isfile', return_value = True ) + def test_GetCompleter_CustomOmniSharpFound_MonoRequiredAndMissing( self, + *args ): + user_options = user_options_store.GetAll().copy( + roslyn_binary_path = 'omnisharp.exe' ) + assert_that( not GetCompleter( user_options ) ) + + + def test_GetCompleter_OmniSharpDefaultOptions( self, *args ): + completer = GetCompleter( user_options_store.GetAll() ) + assert_that( completer._roslyn_path, + equal_to( PATH_TO_OMNISHARP_ROSLYN_BINARY ) ) + + + @patch( 'ycmd.completers.cs.cs_completer.FindExecutableWithFallback', + wraps = lambda x, fb: x if x == 'omnisharp' else None ) + @patch( 'os.path.isfile', return_value = True ) + def test_GetCompleter_OmniSharpFromUserOption_NoMonoNeeded( self, *args ): + user_options = user_options_store.GetAll().copy( + roslyn_binary_path = 'omnisharp' ) + completer = GetCompleter( user_options ) + assert_that( completer._roslyn_path, equal_to( 'omnisharp' ) ) + assert_that( completer.GetCommandLine()[ 0 ], equal_to( 'omnisharp' ) ) + + + @patch( 'ycmd.completers.cs.cs_completer.FindExecutableWithFallback', + wraps = lambda x, fb: x if x in ( 'omnisharp.exe', + 'mono' ) else None ) + @patch( 'os.path.isfile', return_value = True ) + def test_GetCompleter_OmniSharpFromUserOption_MonoNeeded( self, *args ): + user_options = user_options_store.GetAll().copy( + roslyn_binary_path = 'omnisharp.exe', + mono_binary_path = 'mono' ) + completer = GetCompleter( user_options ) + assert_that( completer._roslyn_path, equal_to( 'omnisharp.exe' ) ) + assert_that( completer._mono, equal_to( 'mono' ) ) + assert_that( completer.GetCommandLine()[ 0 ], equal_to( 'mono' ) ) diff --git a/ycmd/tests/cs/server_management_test.py b/ycmd/tests/cs/server_management_test.py new file mode 100644 index 0000000000..837a2387fa --- /dev/null +++ b/ycmd/tests/cs/server_management_test.py @@ -0,0 +1,138 @@ +# Copyright (C) 2024 ycmd contributors +# +# This file is part of ycmd. +# +# ycmd 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. +# +# ycmd 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 ycmd. If not, see . + +from hamcrest import assert_that, contains_exactly, equal_to, has_entry +from unittest.mock import patch +from unittest import TestCase + +from ycmd.completers.language_server.language_server_completer import ( + LanguageServerConnectionTimeout ) +from ycmd.tests.cs import ( PathToTestFile, + IsolatedYcmd, + StartCsCompleterServerInDirectory ) +from ycmd.tests.test_utils import ( BuildRequest, + MockProcessTerminationTimingOut, + WaitUntilCompleterServerReady ) + + +def AssertCsCompleterServerIsRunning( app, is_running ): + request_data = BuildRequest( filetype = 'cs' ) + assert_that( app.post_json( '/debug_info', request_data ).json, + has_entry( + 'completer', + has_entry( 'servers', contains_exactly( + has_entry( 'is_running', is_running ) + ) ) + ) ) + + +class ServerManagementTest( TestCase ): + @IsolatedYcmd() + def test_ServerManagement_RestartServer( self, app ): + filepath = PathToTestFile( 'Program.cs' ) + StartCsCompleterServerInDirectory( app, filepath ) + + AssertCsCompleterServerIsRunning( app, True ) + + app.post_json( + '/run_completer_command', + BuildRequest( + filepath = filepath, + filetype = 'cs', + command_arguments = [ 'RestartServer' ], + ), + ) + + WaitUntilCompleterServerReady( app, 'cs' ) + + AssertCsCompleterServerIsRunning( app, True ) + + + @IsolatedYcmd() + @patch( 'shutil.rmtree', side_effect = OSError ) + @patch( 'ycmd.utils.WaitUntilProcessIsTerminated', + MockProcessTerminationTimingOut ) + def test_ServerManagement_CloseServer_Unclean( self, app, *args ): + StartCsCompleterServerInDirectory( app, PathToTestFile() ) + + app.post_json( + '/run_completer_command', + BuildRequest( + filetype = 'cs', + command_arguments = [ 'StopServer' ] + ) + ) + + request_data = BuildRequest( filetype = 'cs' ) + assert_that( app.post_json( '/debug_info', request_data ).json, + has_entry( + 'completer', + has_entry( 'servers', contains_exactly( + has_entry( 'is_running', False ) + ) ) + ) ) + + + @IsolatedYcmd() + def test_ServerManagement_StopServerTwice( self, app ): + StartCsCompleterServerInDirectory( app, PathToTestFile() ) + + app.post_json( + '/run_completer_command', + BuildRequest( + filetype = 'cs', + command_arguments = [ 'StopServer' ], + ), + ) + + AssertCsCompleterServerIsRunning( app, False ) + + # Stopping a stopped server is a no-op + app.post_json( + '/run_completer_command', + BuildRequest( + filetype = 'cs', + command_arguments = [ 'StopServer' ], + ), + ) + + AssertCsCompleterServerIsRunning( app, False ) + + + @IsolatedYcmd + def test_ServerManagement_StartServer_Fails( self, app ): + with patch( 'ycmd.completers.language_server.language_server_completer.' + 'LanguageServerConnection.AwaitServerConnection', + side_effect = LanguageServerConnectionTimeout ): + resp = app.post_json( '/event_notification', + BuildRequest( + event_name = 'FileReadyToParse', + filetype = 'cs', + filepath = PathToTestFile( 'Program.cs' ), + contents = "" + ) ) + + assert_that( resp.status_code, equal_to( 200 ) ) + + request_data = BuildRequest( filetype = 'cs' ) + assert_that( app.post_json( '/debug_info', request_data ).json, + has_entry( + 'completer', + has_entry( 'servers', contains_exactly( + has_entry( 'is_running', False ) + ) ) + ) ) diff --git a/ycmd/tests/cs/subcommands_test.py b/ycmd/tests/cs/subcommands_test.py index 01db587a32..7835b34905 100644 --- a/ycmd/tests/cs/subcommands_test.py +++ b/ycmd/tests/cs/subcommands_test.py @@ -16,6 +16,7 @@ # along with ycmd. If not, see . from hamcrest import ( assert_that, + contains_inanyorder, empty, has_entries, has_entry, @@ -24,8 +25,9 @@ from unittest.mock import patch from unittest import TestCase import os.path +import requests -from ycmd import user_options_store +from ycmd import handlers, user_options_store from ycmd.tests.cs import setUpModule, tearDownModule # noqa from ycmd.tests.cs import ( IsolatedYcmd, PathToTestFile, @@ -76,6 +78,60 @@ def StopServer_KeepLogFiles( app ): class SubcommandsTest( TestCase ): + @SharedYcmd + def test_Subcommands_DefinedSubcommands( self, app ): + subcommands_data = BuildRequest( completer_target = 'cs' ) + + assert_that( app.post_json( '/defined_subcommands', subcommands_data ).json, + contains_inanyorder( 'FixIt', + 'Format', + 'GetDoc', + 'GetType', + 'GoTo', + 'GoToDeclaration', + 'GoToDefinition', + 'GoToDocumentOutline', + 'GoToImplementation', + 'GoToReferences', + 'GoToSymbol', + 'GoToType', + 'RefactorRename', + 'RestartServer' ) ) + + + @SharedYcmd + def test_Subcommands_ServerNotInitialized( self, app ): + filepath = PathToTestFile( 'testy', 'Program.cs' ) + + completer = handlers._server_state.GetFiletypeCompleter( [ 'cs' ] ) + + @patch.object( completer, '_ServerIsInitialized', return_value = False ) + def Test( app, cmd, arguments ): + request = BuildRequest( completer_target = 'filetype_default', + command_arguments = [ cmd ], + line_num = 1, + column_num = 15, + contents = ReadFile( filepath ), + filetype = 'cs', + filepath = filepath ) + response = app.post_json( '/run_completer_command', request, expect_errors = True ).json + assert_that( response, ErrorMatcher( RuntimeError, 'Server is initializing. Please wait.' ) ) + + Test( app, 'FixIt' ) + Test( app, 'Format' ) + Test( app, 'GetDoc' ) + Test( app, 'GetType' ) + Test( app, 'GoTo' ) + Test( app, 'GoToDeclaration' ) + Test( app, 'GoToDefinition' ) + Test( app, 'GoToDocumentOutline' ) + Test( app, 'GoToImplementation' ) + Test( app, 'GoToReferences' ) + Test( app, 'GoToSymbol' ) + Test( app, 'GoToType' ) + Test( app, 'RefactorRename' ) + + @SharedYcmd def test_Subcommands_FixIt_NoFixitsFound( self, app ): fixit_test = PathToTestFile( 'testy', 'FixItTestCase.cs' ) From 606c481b94ef2c93d0362d88b8f826fd9b0d742e Mon Sep 17 00:00:00 2001 From: Boris Staletic Date: Wed, 21 Aug 2024 19:45:55 +0200 Subject: [PATCH 6/7] Fix flake8 errors --- ycmd/tests/cs/subcommands_test.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ycmd/tests/cs/subcommands_test.py b/ycmd/tests/cs/subcommands_test.py index 7835b34905..4a4fba37d1 100644 --- a/ycmd/tests/cs/subcommands_test.py +++ b/ycmd/tests/cs/subcommands_test.py @@ -25,7 +25,6 @@ from unittest.mock import patch from unittest import TestCase import os.path -import requests from ycmd import handlers, user_options_store from ycmd.tests.cs import setUpModule, tearDownModule # noqa @@ -114,8 +113,12 @@ def Test( app, cmd, arguments ): contents = ReadFile( filepath ), filetype = 'cs', filepath = filepath ) - response = app.post_json( '/run_completer_command', request, expect_errors = True ).json - assert_that( response, ErrorMatcher( RuntimeError, 'Server is initializing. Please wait.' ) ) + response = app.post_json( '/run_completer_command', + request, + expect_errors = True ).json + assert_that( response, + ErrorMatcher( RuntimeError, + 'Server is initializing. Please wait.' ) ) Test( app, 'FixIt' ) Test( app, 'Format' ) From 4e5f77ba45594d97d0a3528ca5d0fc57b218fe18 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 19 Sep 2024 23:42:46 +0100 Subject: [PATCH 7/7] Install dotnet 8 in CI --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2ceec964c8..41be738088 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -239,6 +239,9 @@ jobs: ~/.npm name: Cache test deps + - uses: actions/setup-dotnet@v4 + with: + dotnet-version: '8' - name: Install Java if: matrix.benchmark == false uses: actions/setup-java@v4