postcomplete_test.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. # encoding: utf-8
  2. #
  3. # Copyright (C) 2015-2016 YouCompleteMe contributors
  4. #
  5. # This file is part of YouCompleteMe.
  6. #
  7. # YouCompleteMe is free software: you can redistribute it and/or modify
  8. # it under the terms of the GNU General Public License as published by
  9. # the Free Software Foundation, either version 3 of the License, or
  10. # (at your option) any later version.
  11. #
  12. # YouCompleteMe is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU General Public License
  18. # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
  19. from ycm.tests.test_utils import MockVimModule
  20. MockVimModule()
  21. import contextlib
  22. import json
  23. from hamcrest import assert_that, contains_exactly, empty, equal_to, none
  24. from unittest import TestCase
  25. from unittest.mock import MagicMock, DEFAULT, patch
  26. from ycm import vimsupport
  27. from ycmd.utils import ToBytes
  28. from ycm.client.completion_request import ( CompletionRequest,
  29. _FilterToMatchingCompletions,
  30. _GetRequiredNamespaceImport )
  31. from ycm.client.omni_completion_request import OmniCompletionRequest
  32. def CompleteItemIs( word, abbr = None, menu = None,
  33. info = None, kind = None, **kwargs ):
  34. item = {
  35. 'word': ToBytes( word ),
  36. 'abbr': ToBytes( abbr ),
  37. 'menu': ToBytes( menu ),
  38. 'info': ToBytes( info ),
  39. 'kind': ToBytes( kind ),
  40. }
  41. item.update( **kwargs )
  42. return item
  43. def GetVariableValue_CompleteItemIs( word, abbr = None, menu = None,
  44. info = None, kind = None, **kwargs ):
  45. def Result( variable ):
  46. if variable == 'v:completed_item':
  47. return CompleteItemIs( word, abbr, menu, info, kind, **kwargs )
  48. return DEFAULT
  49. return MagicMock( side_effect = Result )
  50. def BuildCompletion( insertion_text = 'Test',
  51. menu_text = None,
  52. extra_menu_info = None,
  53. detailed_info = None,
  54. kind = None,
  55. extra_data = None ):
  56. completion = {
  57. 'insertion_text': insertion_text
  58. }
  59. if extra_menu_info:
  60. completion[ 'extra_menu_info' ] = extra_menu_info
  61. if menu_text:
  62. completion[ 'menu_text' ] = menu_text
  63. if detailed_info:
  64. completion[ 'detailed_info' ] = detailed_info
  65. if kind:
  66. completion[ 'kind' ] = kind
  67. if extra_data:
  68. completion[ 'extra_data' ] = extra_data
  69. return completion
  70. def BuildCompletionNamespace( namespace = None,
  71. insertion_text = 'Test',
  72. menu_text = None,
  73. extra_menu_info = None,
  74. detailed_info = None,
  75. kind = None ):
  76. return BuildCompletion( insertion_text = insertion_text,
  77. menu_text = menu_text,
  78. extra_menu_info = extra_menu_info,
  79. detailed_info = detailed_info,
  80. kind = kind,
  81. extra_data = {
  82. 'required_namespace_import': namespace
  83. } )
  84. def BuildCompletionFixIt( fixits,
  85. insertion_text = 'Test',
  86. menu_text = None,
  87. extra_menu_info = None,
  88. detailed_info = None,
  89. kind = None ):
  90. return BuildCompletion( insertion_text = insertion_text,
  91. menu_text = menu_text,
  92. extra_menu_info = extra_menu_info,
  93. detailed_info = detailed_info,
  94. kind = kind,
  95. extra_data = {
  96. 'fixits': fixits,
  97. } )
  98. @contextlib.contextmanager
  99. def _SetupForCsharpCompletionDone( completions ):
  100. with patch( 'ycm.vimsupport.InsertNamespace' ):
  101. with _SetUpCompleteDone( completions ) as request:
  102. yield request
  103. @contextlib.contextmanager
  104. def _SetUpCompleteDone( completions ):
  105. with patch( 'ycm.vimsupport.TextBeforeCursor', return_value = ' Test' ):
  106. request = CompletionRequest( None )
  107. request.Done = MagicMock( return_value = True )
  108. request._RawResponse = MagicMock( return_value = {
  109. 'completions': completions
  110. } )
  111. yield request
  112. class PostcompleteTest( TestCase ):
  113. @patch( 'ycm.vimsupport.CurrentFiletypes', return_value = [ 'ycmtest' ] )
  114. def test_OnCompleteDone_DefaultFixIt( self, *args ):
  115. request = CompletionRequest( None )
  116. request.Done = MagicMock( return_value = True )
  117. request._OnCompleteDone_Csharp = MagicMock()
  118. request._OnCompleteDone_FixIt = MagicMock()
  119. request.OnCompleteDone()
  120. request._OnCompleteDone_Csharp.assert_not_called()
  121. request._OnCompleteDone_FixIt.assert_called_once_with()
  122. @patch( 'ycm.vimsupport.CurrentFiletypes', return_value = [ 'cs' ] )
  123. def test_OnCompleteDone_CsharpFixIt( self, *args ):
  124. request = CompletionRequest( None )
  125. request.Done = MagicMock( return_value = True )
  126. request._OnCompleteDone_Csharp = MagicMock()
  127. request._OnCompleteDone_FixIt = MagicMock()
  128. request.OnCompleteDone()
  129. request._OnCompleteDone_Csharp.assert_called_once_with()
  130. request._OnCompleteDone_FixIt.assert_not_called()
  131. @patch( 'ycm.vimsupport.CurrentFiletypes', return_value = [ 'ycmtest' ] )
  132. def test_OnCompleteDone_NoFixItIfNotDone( self, *args ):
  133. request = CompletionRequest( None )
  134. request.Done = MagicMock( return_value = False )
  135. request._OnCompleteDone_Csharp = MagicMock()
  136. request._OnCompleteDone_FixIt = MagicMock()
  137. request.OnCompleteDone()
  138. request._OnCompleteDone_Csharp.assert_not_called()
  139. request._OnCompleteDone_FixIt.assert_not_called()
  140. @patch( 'ycm.vimsupport.CurrentFiletypes', return_value = [ 'ycmtest' ] )
  141. def test_OnCompleteDone_NoFixItForOmnifunc( self, *args ):
  142. request = OmniCompletionRequest( 'omnifunc', None )
  143. request.Done = MagicMock( return_value = True )
  144. request._OnCompleteDone_Csharp = MagicMock()
  145. request._OnCompleteDone_FixIt = MagicMock()
  146. request.OnCompleteDone()
  147. request._OnCompleteDone_Csharp.assert_not_called()
  148. request._OnCompleteDone_FixIt.assert_not_called()
  149. def test_FilterToCompletedCompletions_MatchIsReturned( self ):
  150. completions = [ BuildCompletion( insertion_text = 'Test' ) ]
  151. result = _FilterToMatchingCompletions( CompleteItemIs( 'Test' ),
  152. completions )
  153. assert_that( list( result ), contains_exactly( {} ) )
  154. def test_FilterToCompletedCompletions_ShortTextDoesntRaise( self ):
  155. completions = [ BuildCompletion( insertion_text = 'AAA' ) ]
  156. result = _FilterToMatchingCompletions( CompleteItemIs( 'A' ), completions )
  157. assert_that( list( result ), empty() )
  158. def test_FilterToCompletedCompletions_ExactMatchIsReturned( self ):
  159. completions = [ BuildCompletion( insertion_text = 'Test' ) ]
  160. result = _FilterToMatchingCompletions( CompleteItemIs( 'Test' ),
  161. completions )
  162. assert_that( list( result ), contains_exactly( {} ) )
  163. def test_FilterToCompletedCompletions_NonMatchIsntReturned( self ):
  164. completions = [ BuildCompletion( insertion_text = 'A' ) ]
  165. result = _FilterToMatchingCompletions( CompleteItemIs( ' Quote' ),
  166. completions )
  167. assert_that( list( result ), empty() )
  168. def test_FilterToCompletedCompletions_Unicode( self ):
  169. completions = [ BuildCompletion( insertion_text = '†es†' ) ]
  170. result = _FilterToMatchingCompletions( CompleteItemIs( '†es†' ),
  171. completions )
  172. assert_that( list( result ), contains_exactly( {} ) )
  173. def test_GetRequiredNamespaceImport_ReturnNoneForNoExtraData( self ):
  174. assert_that( _GetRequiredNamespaceImport( {} ), none() )
  175. def test_GetRequiredNamespaceImport_ReturnNamespaceFromExtraData( self ):
  176. namespace = 'A_NAMESPACE'
  177. assert_that( _GetRequiredNamespaceImport(
  178. BuildCompletionNamespace( namespace )[ 'extra_data' ] ),
  179. equal_to( namespace ) )
  180. @patch( 'ycm.vimsupport.GetVariableValue',
  181. GetVariableValue_CompleteItemIs( 'Te' ) )
  182. def test_GetExtraDataUserMayHaveCompleted_ReturnEmptyIfPendingMatches(
  183. *args ):
  184. completions = [ BuildCompletionNamespace( None ) ]
  185. with _SetupForCsharpCompletionDone( completions ) as request:
  186. assert_that( request._GetExtraDataUserMayHaveCompleted(), empty() )
  187. def test_GetExtraDataUserMayHaveCompleted_ReturnMatchIfExactMatches(
  188. self, *args ):
  189. info = [ 'NS', 'Test', 'Abbr', 'Menu', 'Info', 'Kind' ]
  190. completions = [ BuildCompletionNamespace( *info ) ]
  191. with _SetupForCsharpCompletionDone( completions ) as request:
  192. with patch( 'ycm.vimsupport.GetVariableValue',
  193. GetVariableValue_CompleteItemIs( *info[ 1: ] ) ):
  194. assert_that( request._GetExtraDataUserMayHaveCompleted(),
  195. contains_exactly( completions[ 0 ][ 'extra_data' ] ) )
  196. def test_GetExtraDataUserMayHaveCompleted_ReturnMatchIfExactMatchesEvenIfPartial( self ): # noqa
  197. info = [ 'NS', 'Test', 'Abbr', 'Menu', 'Info', 'Kind' ]
  198. completions = [ BuildCompletionNamespace( *info ),
  199. BuildCompletion( insertion_text = 'TestTest' ) ]
  200. with _SetupForCsharpCompletionDone( completions ) as request:
  201. with patch( 'ycm.vimsupport.GetVariableValue',
  202. GetVariableValue_CompleteItemIs( *info[ 1: ] ) ):
  203. assert_that( request._GetExtraDataUserMayHaveCompleted(),
  204. contains_exactly( completions[ 0 ][ 'extra_data' ] ) )
  205. def test_GetExtraDataUserMayHaveCompleted_DontReturnMatchIfNoExactMatchesAndPartial( self ): # noqa
  206. info = [ 'NS', 'Test', 'Abbr', 'Menu', 'Info', 'Kind' ]
  207. completions = [ BuildCompletion( insertion_text = info[ 0 ] ),
  208. BuildCompletion( insertion_text = 'TestTest' ) ]
  209. with _SetupForCsharpCompletionDone( completions ) as request:
  210. with patch( 'ycm.vimsupport.GetVariableValue',
  211. GetVariableValue_CompleteItemIs( *info[ 1: ] ) ):
  212. assert_that( request._GetExtraDataUserMayHaveCompleted(), empty() )
  213. @patch( 'ycm.vimsupport.GetVariableValue',
  214. GetVariableValue_CompleteItemIs( 'Test' ) )
  215. def test_GetExtraDataUserMayHaveCompleted_ReturnMatchIfMatches( self, *args ):
  216. completions = [ BuildCompletionNamespace( None ) ]
  217. with _SetupForCsharpCompletionDone( completions ) as request:
  218. assert_that( request._GetExtraDataUserMayHaveCompleted(),
  219. contains_exactly( completions[ 0 ][ 'extra_data' ] ) )
  220. @patch( 'ycm.vimsupport.GetVariableValue',
  221. GetVariableValue_CompleteItemIs(
  222. 'Test',
  223. user_data=json.dumps( {
  224. 'required_namespace_import': 'namespace1' } ) ) )
  225. def test_GetExtraDataUserMayHaveCompleted_UseUserData0( self, *args ):
  226. # Identical completions but we specify the first one via user_data.
  227. completions = [
  228. BuildCompletionNamespace( 'namespace1' ),
  229. BuildCompletionNamespace( 'namespace2' )
  230. ]
  231. with _SetupForCsharpCompletionDone( completions ) as request:
  232. assert_that(
  233. request._GetExtraDataUserMayHaveCompleted(),
  234. contains_exactly(
  235. BuildCompletionNamespace( 'namespace1' )[ 'extra_data' ] ) )
  236. @patch( 'ycm.vimsupport.GetVariableValue',
  237. GetVariableValue_CompleteItemIs(
  238. 'Test',
  239. user_data=json.dumps( {
  240. 'required_namespace_import': 'namespace2' } ) ) )
  241. def test_GetExtraDataUserMayHaveCompleted_UseUserData1( self, *args ):
  242. # Identical completions but we specify the second one via user_data.
  243. completions = [
  244. BuildCompletionNamespace( 'namespace1' ),
  245. BuildCompletionNamespace( 'namespace2' )
  246. ]
  247. with _SetupForCsharpCompletionDone( completions ) as request:
  248. assert_that(
  249. request._GetExtraDataUserMayHaveCompleted(),
  250. contains_exactly(
  251. BuildCompletionNamespace( 'namespace2' )[ 'extra_data' ] ) )
  252. @patch( 'ycm.vimsupport.GetVariableValue',
  253. GetVariableValue_CompleteItemIs( 'Test', user_data='' ) )
  254. def test_GetExtraDataUserMayHaveCompleted_EmptyUserData( self, *args ):
  255. # Identical completions but none is selected.
  256. completions = [
  257. BuildCompletionNamespace( 'namespace1' ),
  258. BuildCompletionNamespace( 'namespace2' )
  259. ]
  260. with _SetupForCsharpCompletionDone( completions ) as request:
  261. assert_that( request._GetExtraDataUserMayHaveCompleted(), empty() )
  262. @patch( 'ycm.vimsupport.GetVariableValue',
  263. GetVariableValue_CompleteItemIs( 'Test' ) )
  264. def test_PostCompleteCsharp_EmptyDoesntInsertNamespace( self, *args ):
  265. with _SetupForCsharpCompletionDone( [] ) as request:
  266. request._OnCompleteDone_Csharp()
  267. assert_that( not vimsupport.InsertNamespace.called )
  268. @patch( 'ycm.vimsupport.GetVariableValue',
  269. GetVariableValue_CompleteItemIs( 'Test' ) )
  270. def test_PostCompleteCsharp_ExistingWithoutNamespaceDoesntInsertNamespace(
  271. self, *args ):
  272. completions = [ BuildCompletionNamespace( None ) ]
  273. with _SetupForCsharpCompletionDone( completions ) as request:
  274. request._OnCompleteDone_Csharp()
  275. assert_that( not vimsupport.InsertNamespace.called )
  276. @patch( 'ycm.vimsupport.GetVariableValue',
  277. GetVariableValue_CompleteItemIs( 'Test' ) )
  278. def test_PostCompleteCsharp_ValueDoesInsertNamespace( self, *args ):
  279. namespace = 'A_NAMESPACE'
  280. completions = [ BuildCompletionNamespace( namespace ) ]
  281. with _SetupForCsharpCompletionDone( completions ) as request:
  282. request._OnCompleteDone_Csharp()
  283. vimsupport.InsertNamespace.assert_called_once_with( namespace )
  284. @patch( 'ycm.vimsupport.GetVariableValue',
  285. GetVariableValue_CompleteItemIs( 'Test' ) )
  286. @patch( 'ycm.vimsupport.PresentDialog', return_value = 1 )
  287. def test_PostCompleteCsharp_InsertSecondNamespaceIfSelected( self, *args ):
  288. namespace = 'A_NAMESPACE'
  289. namespace2 = 'ANOTHER_NAMESPACE'
  290. completions = [
  291. BuildCompletionNamespace( namespace ),
  292. BuildCompletionNamespace( namespace2 ),
  293. ]
  294. with _SetupForCsharpCompletionDone( completions ) as request:
  295. request._OnCompleteDone_Csharp()
  296. vimsupport.InsertNamespace.assert_called_once_with( namespace2 )
  297. @patch( 'ycm.vimsupport.GetVariableValue',
  298. GetVariableValue_CompleteItemIs( 'Test' ) )
  299. @patch( 'ycm.vimsupport.ReplaceChunks' )
  300. def test_PostCompleteFixIt_ApplyFixIt_NoFixIts( self, replace_chunks, *args ):
  301. completions = [
  302. BuildCompletionFixIt( [] )
  303. ]
  304. with _SetUpCompleteDone( completions ) as request:
  305. request._OnCompleteDone_FixIt()
  306. replace_chunks.assert_not_called()
  307. @patch( 'ycm.vimsupport.GetVariableValue',
  308. GetVariableValue_CompleteItemIs( 'Test' ) )
  309. @patch( 'ycm.vimsupport.ReplaceChunks' )
  310. def test_PostCompleteFixIt_ApplyFixIt_EmptyFixIt(
  311. self, replace_chunks, *args ):
  312. completions = [
  313. BuildCompletionFixIt( [ { 'chunks': [] } ] )
  314. ]
  315. with _SetUpCompleteDone( completions ) as request:
  316. request._OnCompleteDone_FixIt()
  317. replace_chunks.assert_called_once_with( [], silent = True )
  318. @patch( 'ycm.vimsupport.GetVariableValue',
  319. GetVariableValue_CompleteItemIs( 'Test' ) )
  320. @patch( 'ycm.vimsupport.ReplaceChunks' )
  321. def test_PostCompleteFixIt_ApplyFixIt_NoFixIt( self, replace_chunks, *args ):
  322. completions = [
  323. BuildCompletion()
  324. ]
  325. with _SetUpCompleteDone( completions ) as request:
  326. request._OnCompleteDone_FixIt()
  327. replace_chunks.assert_not_called()
  328. @patch( 'ycm.vimsupport.GetVariableValue',
  329. GetVariableValue_CompleteItemIs( 'Test' ) )
  330. @patch( 'ycm.vimsupport.ReplaceChunks' )
  331. def test_PostCompleteFixIt_ApplyFixIt_PickFirst(
  332. self, replace_chunks, *args ):
  333. completions = [
  334. BuildCompletionFixIt( [ { 'chunks': 'one' } ] ),
  335. BuildCompletionFixIt( [ { 'chunks': 'two' } ] ),
  336. ]
  337. with _SetUpCompleteDone( completions ) as request:
  338. request._OnCompleteDone_FixIt()
  339. replace_chunks.assert_called_once_with( 'one', silent = True )
  340. @patch( 'ycm.vimsupport.GetVariableValue',
  341. GetVariableValue_CompleteItemIs(
  342. 'Test',
  343. user_data=json.dumps( { 'fixits': [ { 'chunks': 'one' } ] } ) ) )
  344. @patch( 'ycm.vimsupport.ReplaceChunks' )
  345. def test_PostCompleteFixIt_ApplyFixIt_PickFirstUserData( self,
  346. replace_chunks,
  347. *args ):
  348. completions = [
  349. BuildCompletionFixIt( [ { 'chunks': 'one' } ] ),
  350. BuildCompletionFixIt( [ { 'chunks': 'two' } ] ),
  351. ]
  352. with _SetUpCompleteDone( completions ) as request:
  353. request._OnCompleteDone_FixIt()
  354. replace_chunks.assert_called_once_with( 'one', silent = True )
  355. @patch( 'ycm.vimsupport.GetVariableValue',
  356. GetVariableValue_CompleteItemIs(
  357. 'Test',
  358. user_data=json.dumps( { 'fixits': [ { 'chunks': 'two' } ] } ) ) )
  359. @patch( 'ycm.vimsupport.ReplaceChunks' )
  360. def test_PostCompleteFixIt_ApplyFixIt_PickSecond(
  361. self, replace_chunks, *args ):
  362. completions = [
  363. BuildCompletionFixIt( [ { 'chunks': 'one' } ] ),
  364. BuildCompletionFixIt( [ { 'chunks': 'two' } ] ),
  365. ]
  366. with _SetUpCompleteDone( completions ) as request:
  367. request._OnCompleteDone_FixIt()
  368. replace_chunks.assert_called_once_with( 'two', silent = True )