event_notification_test.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587
  1. # coding: utf-8
  2. #
  3. # Copyright (C) 2015-2018 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 __future__ import unicode_literals
  20. from __future__ import print_function
  21. from __future__ import division
  22. from __future__ import absolute_import
  23. # Not installing aliases from python-future; it's unreliable and slow.
  24. from builtins import * # noqa
  25. from ycm.tests.test_utils import ( CurrentWorkingDirectory, ExtendedMock,
  26. MockVimBuffers, MockVimModule, VimBuffer,
  27. VimSign )
  28. MockVimModule()
  29. import contextlib
  30. import os
  31. from ycm.tests import ( PathToTestFile, test_utils, YouCompleteMeInstance,
  32. WaitUntilReady )
  33. from ycm.vimsupport import SIGN_BUFFER_ID_INITIAL_VALUE
  34. from ycmd.responses import ( BuildDiagnosticData, Diagnostic, Location, Range,
  35. UnknownExtraConf, ServerError )
  36. from hamcrest import ( assert_that, contains, empty, has_entries, has_entry,
  37. has_item, has_items, has_key, is_not )
  38. from mock import call, MagicMock, patch
  39. from nose.tools import eq_, ok_
  40. def PresentDialog_Confirm_Call( message ):
  41. """Return a mock.call object for a call to vimsupport.PresentDialog, as called
  42. why vimsupport.Confirm with the supplied confirmation message"""
  43. return call( message, [ 'Ok', 'Cancel' ] )
  44. @contextlib.contextmanager
  45. def MockArbitraryBuffer( filetype ):
  46. """Used via the with statement, set up a single buffer with an arbitrary name
  47. and no contents. Its filetype is set to the supplied filetype."""
  48. # Arbitrary, but valid, single buffer open.
  49. current_buffer = VimBuffer( os.path.realpath( 'TEST_BUFFER' ),
  50. window = 1,
  51. filetype = filetype )
  52. with MockVimBuffers( [ current_buffer ], current_buffer ):
  53. yield
  54. @contextlib.contextmanager
  55. def MockEventNotification( response_method, native_filetype_completer = True ):
  56. """Mock out the EventNotification client request object, replacing the
  57. Response handler's JsonFromFuture with the supplied |response_method|.
  58. Additionally mock out YouCompleteMe's FiletypeCompleterExistsForFiletype
  59. method to return the supplied |native_filetype_completer| parameter, rather
  60. than querying the server"""
  61. # We don't want the event to actually be sent to the server, just have it
  62. # return success
  63. with patch( 'ycm.client.event_notification.EventNotification.'
  64. 'PostDataToHandlerAsync',
  65. return_value = MagicMock( return_value=True ) ):
  66. # We set up a fake response (as called by EventNotification.Response) which
  67. # calls the supplied callback method. Generally this callback just raises an
  68. # apropriate exception, otherwise it would have to return a mock future
  69. # object.
  70. #
  71. # Note: JsonFromFuture is actually part of ycm.client.base_request, but we
  72. # must patch where an object is looked up, not where it is defined. See
  73. # https://docs.python.org/dev/library/unittest.mock.html#where-to-patch for
  74. # details.
  75. with patch( 'ycm.client.event_notification.JsonFromFuture',
  76. side_effect = response_method ):
  77. # Filetype available information comes from the server, so rather than
  78. # relying on that request, we mock out the check. The caller decides if
  79. # filetype completion is available
  80. with patch(
  81. 'ycm.youcompleteme.YouCompleteMe.FiletypeCompleterExistsForFiletype',
  82. return_value = native_filetype_completer ):
  83. yield
  84. @patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock )
  85. @YouCompleteMeInstance()
  86. def EventNotification_FileReadyToParse_NonDiagnostic_Error_test(
  87. ycm, post_vim_message ):
  88. # This test validates the behaviour of YouCompleteMe.HandleFileParseRequest
  89. # in combination with YouCompleteMe.OnFileReadyToParse when the completer
  90. # raises an exception handling FileReadyToParse event notification
  91. ERROR_TEXT = 'Some completer response text'
  92. def ErrorResponse( *args ):
  93. raise ServerError( ERROR_TEXT )
  94. with MockArbitraryBuffer( 'javascript' ):
  95. with MockEventNotification( ErrorResponse ):
  96. ycm.OnFileReadyToParse()
  97. ok_( ycm.FileParseRequestReady() )
  98. ycm.HandleFileParseRequest()
  99. # The first call raises a warning
  100. post_vim_message.assert_has_exact_calls( [
  101. call( ERROR_TEXT, truncate = True )
  102. ] )
  103. # Subsequent calls don't re-raise the warning
  104. ycm.HandleFileParseRequest()
  105. post_vim_message.assert_has_exact_calls( [
  106. call( ERROR_TEXT, truncate = True )
  107. ] )
  108. # But it does if a subsequent event raises again
  109. ycm.OnFileReadyToParse()
  110. ok_( ycm.FileParseRequestReady() )
  111. ycm.HandleFileParseRequest()
  112. post_vim_message.assert_has_exact_calls( [
  113. call( ERROR_TEXT, truncate = True ),
  114. call( ERROR_TEXT, truncate = True )
  115. ] )
  116. @YouCompleteMeInstance()
  117. def EventNotification_FileReadyToParse_NonDiagnostic_Error_NonNative_test(
  118. ycm ):
  119. test_utils.VIM_MATCHES = []
  120. test_utils.VIM_SIGNS = []
  121. with MockArbitraryBuffer( 'javascript' ):
  122. with MockEventNotification( None, False ):
  123. ycm.OnFileReadyToParse()
  124. ycm.HandleFileParseRequest()
  125. assert_that(
  126. test_utils.VIM_MATCHES,
  127. contains()
  128. )
  129. assert_that(
  130. test_utils.VIM_SIGNS,
  131. contains()
  132. )
  133. @patch( 'ycm.client.base_request._LoadExtraConfFile',
  134. new_callable = ExtendedMock )
  135. @patch( 'ycm.client.base_request._IgnoreExtraConfFile',
  136. new_callable = ExtendedMock )
  137. @YouCompleteMeInstance()
  138. def EventNotification_FileReadyToParse_NonDiagnostic_ConfirmExtraConf_test(
  139. ycm, ignore_extra_conf, load_extra_conf ):
  140. # This test validates the behaviour of YouCompleteMe.HandleFileParseRequest
  141. # in combination with YouCompleteMe.OnFileReadyToParse when the completer
  142. # raises the (special) UnknownExtraConf exception
  143. FILE_NAME = 'a_file'
  144. MESSAGE = ( 'Found ' + FILE_NAME + '. Load? \n\n(Question can be '
  145. 'turned off with options, see YCM docs)' )
  146. def UnknownExtraConfResponse( *args ):
  147. raise UnknownExtraConf( FILE_NAME )
  148. with MockArbitraryBuffer( 'javascript' ):
  149. with MockEventNotification( UnknownExtraConfResponse ):
  150. # When the user accepts the extra conf, we load it
  151. with patch( 'ycm.vimsupport.PresentDialog',
  152. return_value = 0,
  153. new_callable = ExtendedMock ) as present_dialog:
  154. ycm.OnFileReadyToParse()
  155. ok_( ycm.FileParseRequestReady() )
  156. ycm.HandleFileParseRequest()
  157. present_dialog.assert_has_exact_calls( [
  158. PresentDialog_Confirm_Call( MESSAGE ),
  159. ] )
  160. load_extra_conf.assert_has_exact_calls( [
  161. call( FILE_NAME ),
  162. ] )
  163. # Subsequent calls don't re-raise the warning
  164. ycm.HandleFileParseRequest()
  165. present_dialog.assert_has_exact_calls( [
  166. PresentDialog_Confirm_Call( MESSAGE )
  167. ] )
  168. load_extra_conf.assert_has_exact_calls( [
  169. call( FILE_NAME ),
  170. ] )
  171. # But it does if a subsequent event raises again
  172. ycm.OnFileReadyToParse()
  173. ok_( ycm.FileParseRequestReady() )
  174. ycm.HandleFileParseRequest()
  175. present_dialog.assert_has_exact_calls( [
  176. PresentDialog_Confirm_Call( MESSAGE ),
  177. PresentDialog_Confirm_Call( MESSAGE ),
  178. ] )
  179. load_extra_conf.assert_has_exact_calls( [
  180. call( FILE_NAME ),
  181. call( FILE_NAME ),
  182. ] )
  183. # When the user rejects the extra conf, we reject it
  184. with patch( 'ycm.vimsupport.PresentDialog',
  185. return_value = 1,
  186. new_callable = ExtendedMock ) as present_dialog:
  187. ycm.OnFileReadyToParse()
  188. ok_( ycm.FileParseRequestReady() )
  189. ycm.HandleFileParseRequest()
  190. present_dialog.assert_has_exact_calls( [
  191. PresentDialog_Confirm_Call( MESSAGE ),
  192. ] )
  193. ignore_extra_conf.assert_has_exact_calls( [
  194. call( FILE_NAME ),
  195. ] )
  196. # Subsequent calls don't re-raise the warning
  197. ycm.HandleFileParseRequest()
  198. present_dialog.assert_has_exact_calls( [
  199. PresentDialog_Confirm_Call( MESSAGE )
  200. ] )
  201. ignore_extra_conf.assert_has_exact_calls( [
  202. call( FILE_NAME ),
  203. ] )
  204. # But it does if a subsequent event raises again
  205. ycm.OnFileReadyToParse()
  206. ok_( ycm.FileParseRequestReady() )
  207. ycm.HandleFileParseRequest()
  208. present_dialog.assert_has_exact_calls( [
  209. PresentDialog_Confirm_Call( MESSAGE ),
  210. PresentDialog_Confirm_Call( MESSAGE ),
  211. ] )
  212. ignore_extra_conf.assert_has_exact_calls( [
  213. call( FILE_NAME ),
  214. call( FILE_NAME ),
  215. ] )
  216. @YouCompleteMeInstance()
  217. def EventNotification_FileReadyToParse_Diagnostic_Error_Native_test( ycm ):
  218. test_utils.VIM_SIGNS = []
  219. _Check_FileReadyToParse_Diagnostic_Error( ycm )
  220. _Check_FileReadyToParse_Diagnostic_Warning( ycm )
  221. _Check_FileReadyToParse_Diagnostic_Clean( ycm )
  222. def _Check_FileReadyToParse_Diagnostic_Error( ycm ):
  223. # Tests Vim sign placement and error/warning count python API
  224. # when one error is returned.
  225. def DiagnosticResponse( *args ):
  226. start = Location( 1, 2, 'TEST_BUFFER' )
  227. end = Location( 1, 4, 'TEST_BUFFER' )
  228. extent = Range( start, end )
  229. diagnostic = Diagnostic( [], start, extent, 'expected ;', 'ERROR' )
  230. return [ BuildDiagnosticData( diagnostic ) ]
  231. with MockArbitraryBuffer( 'cpp' ):
  232. with MockEventNotification( DiagnosticResponse ):
  233. ycm.OnFileReadyToParse()
  234. ok_( ycm.FileParseRequestReady() )
  235. ycm.HandleFileParseRequest()
  236. assert_that(
  237. test_utils.VIM_SIGNS,
  238. contains(
  239. VimSign( SIGN_BUFFER_ID_INITIAL_VALUE, 1, 'YcmError', 1 )
  240. )
  241. )
  242. eq_( ycm.GetErrorCount(), 1 )
  243. eq_( ycm.GetWarningCount(), 0 )
  244. # Consequent calls to HandleFileParseRequest shouldn't mess with
  245. # existing diagnostics, when there is no new parse request.
  246. ycm.HandleFileParseRequest()
  247. assert_that(
  248. test_utils.VIM_SIGNS,
  249. contains(
  250. VimSign( SIGN_BUFFER_ID_INITIAL_VALUE, 1, 'YcmError', 1 )
  251. )
  252. )
  253. eq_( ycm.GetErrorCount(), 1 )
  254. eq_( ycm.GetWarningCount(), 0 )
  255. # New identical requests should result in the same diagnostics.
  256. ycm.OnFileReadyToParse()
  257. ok_( ycm.FileParseRequestReady() )
  258. ycm.HandleFileParseRequest()
  259. assert_that(
  260. test_utils.VIM_SIGNS,
  261. contains(
  262. VimSign( SIGN_BUFFER_ID_INITIAL_VALUE, 1, 'YcmError', 1 )
  263. )
  264. )
  265. eq_( ycm.GetErrorCount(), 1 )
  266. eq_( ycm.GetWarningCount(), 0 )
  267. def _Check_FileReadyToParse_Diagnostic_Warning( ycm ):
  268. # Tests Vim sign placement/unplacement and error/warning count python API
  269. # when one warning is returned.
  270. # Should be called after _Check_FileReadyToParse_Diagnostic_Error
  271. def DiagnosticResponse( *args ):
  272. start = Location( 2, 2, 'TEST_BUFFER' )
  273. end = Location( 2, 4, 'TEST_BUFFER' )
  274. extent = Range( start, end )
  275. diagnostic = Diagnostic( [], start, extent, 'cast', 'WARNING' )
  276. return [ BuildDiagnosticData( diagnostic ) ]
  277. with MockArbitraryBuffer( 'cpp' ):
  278. with MockEventNotification( DiagnosticResponse ):
  279. ycm.OnFileReadyToParse()
  280. ok_( ycm.FileParseRequestReady() )
  281. ycm.HandleFileParseRequest()
  282. assert_that(
  283. test_utils.VIM_SIGNS,
  284. contains(
  285. VimSign( SIGN_BUFFER_ID_INITIAL_VALUE + 1, 2, 'YcmWarning', 1 )
  286. )
  287. )
  288. eq_( ycm.GetErrorCount(), 0 )
  289. eq_( ycm.GetWarningCount(), 1 )
  290. # Consequent calls to HandleFileParseRequest shouldn't mess with
  291. # existing diagnostics, when there is no new parse request.
  292. ycm.HandleFileParseRequest()
  293. assert_that(
  294. test_utils.VIM_SIGNS,
  295. contains(
  296. VimSign( SIGN_BUFFER_ID_INITIAL_VALUE + 1, 2, 'YcmWarning', 1 )
  297. )
  298. )
  299. eq_( ycm.GetErrorCount(), 0 )
  300. eq_( ycm.GetWarningCount(), 1 )
  301. def _Check_FileReadyToParse_Diagnostic_Clean( ycm ):
  302. # Tests Vim sign unplacement and error/warning count python API
  303. # when there are no errors/warnings left.
  304. # Should be called after _Check_FileReadyToParse_Diagnostic_Warning
  305. with MockArbitraryBuffer( 'cpp' ):
  306. with MockEventNotification( MagicMock( return_value = [] ) ):
  307. ycm.OnFileReadyToParse()
  308. ycm.HandleFileParseRequest()
  309. assert_that(
  310. test_utils.VIM_SIGNS,
  311. empty()
  312. )
  313. eq_( ycm.GetErrorCount(), 0 )
  314. eq_( ycm.GetWarningCount(), 0 )
  315. @patch( 'ycm.youcompleteme.YouCompleteMe._AddUltiSnipsDataIfNeeded' )
  316. @YouCompleteMeInstance( { 'collect_identifiers_from_tags_files': 1 } )
  317. def EventNotification_FileReadyToParse_TagFiles_UnicodeWorkingDirectory_test(
  318. ycm, *args ):
  319. unicode_dir = PathToTestFile( 'uni¢𐍈d€' )
  320. current_buffer_file = PathToTestFile( 'uni¢𐍈d€', 'current_buffer' )
  321. current_buffer = VimBuffer( name = current_buffer_file,
  322. contents = [ 'current_buffer_contents' ],
  323. filetype = 'some_filetype' )
  324. with patch( 'ycm.client.event_notification.EventNotification.'
  325. 'PostDataToHandlerAsync' ) as post_data_to_handler_async:
  326. with CurrentWorkingDirectory( unicode_dir ):
  327. with MockVimBuffers( [ current_buffer ], current_buffer, ( 1, 5 ) ):
  328. ycm.OnFileReadyToParse()
  329. assert_that(
  330. # Positional arguments passed to PostDataToHandlerAsync.
  331. post_data_to_handler_async.call_args[ 0 ],
  332. contains(
  333. has_entries( {
  334. 'filepath': current_buffer_file,
  335. 'line_num': 1,
  336. 'column_num': 6,
  337. 'file_data': has_entries( {
  338. current_buffer_file: has_entries( {
  339. 'contents': 'current_buffer_contents\n',
  340. 'filetypes': [ 'some_filetype' ]
  341. } )
  342. } ),
  343. 'event_name': 'FileReadyToParse',
  344. 'tag_files': has_item( PathToTestFile( 'uni¢𐍈d€', 'tags' ) )
  345. } ),
  346. 'event_notification'
  347. )
  348. )
  349. @patch( 'ycm.youcompleteme.YouCompleteMe._AddUltiSnipsDataIfNeeded' )
  350. @YouCompleteMeInstance()
  351. def EventNotification_BufferVisit_BuildRequestForCurrentAndUnsavedBuffers_test(
  352. ycm, *args ):
  353. current_buffer_file = os.path.realpath( 'current_buffer' )
  354. current_buffer = VimBuffer( name = current_buffer_file,
  355. number = 1,
  356. contents = [ 'current_buffer_contents' ],
  357. filetype = 'some_filetype',
  358. modified = False )
  359. modified_buffer_file = os.path.realpath( 'modified_buffer' )
  360. modified_buffer = VimBuffer( name = modified_buffer_file,
  361. number = 2,
  362. contents = [ 'modified_buffer_contents' ],
  363. filetype = 'some_filetype',
  364. modified = True )
  365. unmodified_buffer_file = os.path.realpath( 'unmodified_buffer' )
  366. unmodified_buffer = VimBuffer( name = unmodified_buffer_file,
  367. number = 3,
  368. contents = [ 'unmodified_buffer_contents' ],
  369. filetype = 'some_filetype',
  370. modified = False )
  371. with patch( 'ycm.client.event_notification.EventNotification.'
  372. 'PostDataToHandlerAsync' ) as post_data_to_handler_async:
  373. with MockVimBuffers( [ current_buffer, modified_buffer, unmodified_buffer ],
  374. current_buffer,
  375. ( 1, 5 ) ):
  376. ycm.OnBufferVisit()
  377. assert_that(
  378. # Positional arguments passed to PostDataToHandlerAsync.
  379. post_data_to_handler_async.call_args[ 0 ],
  380. contains(
  381. has_entries( {
  382. 'filepath': current_buffer_file,
  383. 'line_num': 1,
  384. 'column_num': 6,
  385. 'file_data': has_entries( {
  386. current_buffer_file: has_entries( {
  387. 'contents': 'current_buffer_contents\n',
  388. 'filetypes': [ 'some_filetype' ]
  389. } ),
  390. modified_buffer_file: has_entries( {
  391. 'contents': 'modified_buffer_contents\n',
  392. 'filetypes': [ 'some_filetype' ]
  393. } )
  394. } ),
  395. 'event_name': 'BufferVisit'
  396. } ),
  397. 'event_notification'
  398. )
  399. )
  400. @YouCompleteMeInstance()
  401. def EventNotification_BufferUnload_BuildRequestForDeletedAndUnsavedBuffers_test(
  402. ycm ):
  403. current_buffer_file = os.path.realpath( 'current_βuffer' )
  404. current_buffer = VimBuffer( name = current_buffer_file,
  405. number = 1,
  406. contents = [ 'current_buffer_contents' ],
  407. filetype = 'some_filetype',
  408. modified = True )
  409. deleted_buffer_file = os.path.realpath( 'deleted_βuffer' )
  410. deleted_buffer = VimBuffer( name = deleted_buffer_file,
  411. number = 2,
  412. contents = [ 'deleted_buffer_contents' ],
  413. filetype = 'some_filetype',
  414. modified = False )
  415. with patch( 'ycm.client.event_notification.EventNotification.'
  416. 'PostDataToHandlerAsync' ) as post_data_to_handler_async:
  417. with MockVimBuffers( [ current_buffer, deleted_buffer ], current_buffer ):
  418. ycm.OnBufferUnload( deleted_buffer.number )
  419. assert_that(
  420. # Positional arguments passed to PostDataToHandlerAsync.
  421. post_data_to_handler_async.call_args[ 0 ],
  422. contains(
  423. has_entries( {
  424. 'filepath': deleted_buffer_file,
  425. 'line_num': 1,
  426. 'column_num': 1,
  427. 'file_data': has_entries( {
  428. current_buffer_file: has_entries( {
  429. 'contents': 'current_buffer_contents\n',
  430. 'filetypes': [ 'some_filetype' ]
  431. } ),
  432. deleted_buffer_file: has_entries( {
  433. 'contents': 'deleted_buffer_contents\n',
  434. 'filetypes': [ 'some_filetype' ]
  435. } )
  436. } ),
  437. 'event_name': 'BufferUnload'
  438. } ),
  439. 'event_notification'
  440. )
  441. )
  442. @patch( 'ycm.syntax_parse.SyntaxKeywordsForCurrentBuffer',
  443. return_value = [ 'foo', 'bar' ] )
  444. @YouCompleteMeInstance( { 'seed_identifiers_with_syntax': 1 } )
  445. def EventNotification_FileReadyToParse_SyntaxKeywords_SeedWithCache_test(
  446. ycm, *args ):
  447. current_buffer = VimBuffer( name = 'current_buffer',
  448. filetype = 'some_filetype' )
  449. with patch( 'ycm.client.event_notification.EventNotification.'
  450. 'PostDataToHandlerAsync' ) as post_data_to_handler_async:
  451. with MockVimBuffers( [ current_buffer ], current_buffer ):
  452. ycm.OnFileReadyToParse()
  453. assert_that(
  454. # Positional arguments passed to PostDataToHandlerAsync.
  455. post_data_to_handler_async.call_args[ 0 ],
  456. contains(
  457. has_entry( 'syntax_keywords', has_items( 'foo', 'bar' ) ),
  458. 'event_notification'
  459. )
  460. )
  461. # Do not send again syntax keywords in subsequent requests.
  462. ycm.OnFileReadyToParse()
  463. assert_that(
  464. # Positional arguments passed to PostDataToHandlerAsync.
  465. post_data_to_handler_async.call_args[ 0 ],
  466. contains(
  467. is_not( has_key( 'syntax_keywords' ) ),
  468. 'event_notification'
  469. )
  470. )
  471. @patch( 'ycm.syntax_parse.SyntaxKeywordsForCurrentBuffer',
  472. return_value = [ 'foo', 'bar' ] )
  473. @YouCompleteMeInstance( { 'seed_identifiers_with_syntax': 1 } )
  474. def EventNotification_FileReadyToParse_SyntaxKeywords_ClearCacheIfRestart_test(
  475. ycm, *args ):
  476. current_buffer = VimBuffer( name = 'current_buffer',
  477. filetype = 'some_filetype' )
  478. with patch( 'ycm.client.event_notification.EventNotification.'
  479. 'PostDataToHandlerAsync' ) as post_data_to_handler_async:
  480. with MockVimBuffers( [ current_buffer ], current_buffer ):
  481. ycm.OnFileReadyToParse()
  482. assert_that(
  483. # Positional arguments passed to PostDataToHandlerAsync.
  484. post_data_to_handler_async.call_args[ 0 ],
  485. contains(
  486. has_entry( 'syntax_keywords', has_items( 'foo', 'bar' ) ),
  487. 'event_notification'
  488. )
  489. )
  490. # Send again the syntax keywords after restarting the server.
  491. ycm.RestartServer()
  492. WaitUntilReady()
  493. ycm.OnFileReadyToParse()
  494. assert_that(
  495. # Positional arguments passed to PostDataToHandlerAsync.
  496. post_data_to_handler_async.call_args[ 0 ],
  497. contains(
  498. has_entry( 'syntax_keywords', has_items( 'foo', 'bar' ) ),
  499. 'event_notification'
  500. )
  501. )