1
0

event_notification_test.py 21 KB

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