diagnostics.test.vim 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. function! SetUp()
  2. let g:ycm_use_clangd = 1
  3. let g:ycm_confirm_extra_conf = 0
  4. let g:ycm_auto_trigger = 1
  5. let g:ycm_keep_logfiles = 1
  6. let g:ycm_log_level = 'DEBUG'
  7. let g:ycm_always_populate_location_list = 1
  8. let g:ycm_enable_semantic_highlighting = 1
  9. let g:ycm_auto_hover = ''
  10. " diagnostics take ages
  11. let g:ycm_test_min_delay = 7
  12. call youcompleteme#test#setup#SetUp()
  13. endfunction
  14. function! TearDown()
  15. call youcompleteme#test#setup#CleanUp()
  16. endfunction
  17. function! Test_Diagnostics_Update_In_Insert_Mode()
  18. call youcompleteme#test#setup#OpenFile(
  19. \ '/test/testdata/cpp/new_file.cpp', {} )
  20. " Required to trigger TextChangedI
  21. " https://github.com/vim/vim/issues/4665#event-2480928194
  22. call test_override( 'char_avail', 1 )
  23. " Must do the checks in a timer callback because we need to stay in insert
  24. " mode until done.
  25. function! Check( id ) closure
  26. call WaitForAssert( {-> assert_true( len( sign_getplaced(
  27. \ '%',
  28. \ { 'group': 'ycm_signs' } )[ 0 ][ 'signs' ] ) ) } )
  29. call feedkeys( "\<ESC>" )
  30. endfunction
  31. call FeedAndCheckMain( 'imain(', funcref( 'Check' ) )
  32. call test_override( 'ALL', 0 )
  33. endfunction
  34. function! SetUp_Test_Disable_Diagnostics_Update_In_insert_Mode()
  35. call youcompleteme#test#setup#PushGlobal(
  36. \ 'ycm_update_diagnostics_in_insert_mode', 0 )
  37. endfunction
  38. function! Test_Disable_Diagnostics_Update_In_insert_Mode()
  39. call youcompleteme#test#setup#OpenFile(
  40. \ '/test/testdata/cpp/new_file.cpp', {} )
  41. " Required to trigger TextChangedI
  42. " https://github.com/vim/vim/issues/4665#event-2480928194
  43. call test_override( 'char_avail', 1 )
  44. " Must do the checks in a timer callback because we need to stay in insert
  45. " mode until done.
  46. function! CheckNoDiagUIAfterOpenParenthesis( id ) closure
  47. call WaitForAssert( {->
  48. \ assert_true(
  49. \ py3eval(
  50. \ 'len( ycm_state.CurrentBuffer()._diag_interface._diagnostics )'
  51. \ ) ) } )
  52. call WaitForAssert( {-> assert_false( len( sign_getplaced(
  53. \ '%',
  54. \ { 'group': 'ycm_signs' } )[ 0 ][ 'signs' ] ) ) } )
  55. call FeedAndCheckAgain( " \<BS>\<BS>\<BS>)",
  56. \ funcref( 'CheckNoDiagUIAfterClosingPatenthesis' ) )
  57. endfunction
  58. function! CheckNoDiagUIAfterClosingPatenthesis( id ) closure
  59. call WaitForAssert( {->
  60. \ assert_true(
  61. \ py3eval(
  62. \ 'len( ycm_state.CurrentBuffer()._diag_interface._diagnostics )'
  63. \ ) ) } )
  64. call WaitForAssert( {-> assert_false( len( sign_getplaced(
  65. \ '%',
  66. \ { 'group': 'ycm_signs' } )[ 0 ][ 'signs' ] ) ) } )
  67. call feedkeys( "\<ESC>" )
  68. call FeedAndCheckAgain( "\<ESC>",
  69. \ funcref( 'CheckDiagUIRefreshedAfterLeavingInsertMode' ) )
  70. endfunction
  71. function! CheckDiagUIRefreshedAfterLeavingInsertMode( id ) closure
  72. call WaitForAssert( {->
  73. \ assert_true(
  74. \ py3eval(
  75. \ 'len( ycm_state.CurrentBuffer()._diag_interface._diagnostics )'
  76. \ ) ) } )
  77. call WaitForAssert( {-> assert_true( len( sign_getplaced(
  78. \ '%',
  79. \ { 'group': 'ycm_signs' } )[ 0 ][ 'signs' ] ) ) } )
  80. call FeedAndCheckAgain( "A\<CR>", funcref( 'CheckNoPropsAfterNewLine' ) )
  81. endfunction
  82. function! CheckNoPropsAfterNewLine( id ) closure
  83. call WaitForAssert( {->
  84. \ assert_true(
  85. \ py3eval(
  86. \ 'len( ycm_state.CurrentBuffer()._diag_interface._diagnostics )'
  87. \ ) ) } )
  88. call WaitForAssert( {-> assert_false( len( prop_list(
  89. \ 1, { 'end_lnum': -1,
  90. \ 'types': [ 'YcmVirtDiagWarning',
  91. \ 'YcmVirtDiagError',
  92. \ 'YcmVirtDiagPadding' ] } ) ) ) } )
  93. endfunction
  94. call FeedAndCheckMain( 'imain(',
  95. \ funcref( 'CheckNoDiagUIAfterOpenParenthesis' ) )
  96. call test_override( 'ALL', 0 )
  97. endfunction
  98. function! TearDown_Test_Disable_Diagnostics_Update_In_insert_Mode()
  99. call youcompleteme#test#setup#PopGlobal(
  100. \ 'ycm_update_diagnostics_in_insert_mode' )
  101. endfunction
  102. function! Test_Changing_Filetype_Refreshes_Diagnostics()
  103. call youcompleteme#test#setup#OpenFile(
  104. \ '/test/testdata/diagnostics/foo.xml',
  105. \ { 'native_ft': 0 } )
  106. call assert_equal( 'xml', &filetype )
  107. call assert_false(
  108. \ pyxeval( 'ycm_state._buffers[' . bufnr( '%' ) . ']._async_diags' ) )
  109. call assert_true( empty( sign_getplaced(
  110. \ '%',
  111. \ { 'group': 'ycm_signs' } )[ 0 ][ 'signs' ] ) )
  112. setf typescript
  113. call assert_equal( 'typescript', &filetype )
  114. call assert_false(
  115. \ pyxeval( 'ycm_state._buffers[' . bufnr( '%' ) . ']._async_diags' ) )
  116. " Diagnostics are async, so wait for the assert to return 0 for a while.
  117. call WaitForAssert( {-> assert_equal( 1, len( sign_getplaced(
  118. \ '%',
  119. \ { 'group': 'ycm_signs' } )[ 0 ][ 'signs' ] ) ) } )
  120. call assert_equal( 1, len( sign_getplaced(
  121. \ '%',
  122. \ { 'group': 'ycm_signs' } )[ 0 ][ 'signs' ] ) )
  123. call assert_equal(
  124. \ 'YcmError',
  125. \ sign_getplaced(
  126. \ '%',
  127. \ { 'group': 'ycm_signs' } )[ 0 ][ 'signs' ][ 0 ][ 'name' ] )
  128. call assert_false( empty( getloclist( 0 ) ) )
  129. endfunction
  130. function! Test_MessagePoll_After_LocationList()
  131. call youcompleteme#test#setup#OpenFile(
  132. \ '/test/testdata/diagnostics/foo.cpp', {} )
  133. setf cpp
  134. call assert_equal( 'cpp', &filetype )
  135. call WaitForAssert( {-> assert_equal( 2, len( sign_getplaced(
  136. \ '%',
  137. \ { 'group': 'ycm_signs' } )[ 0 ][ 'signs' ] ) ) } )
  138. call setline( 1, '' )
  139. " Wait for the parse request to be complete otherwise we won't send another
  140. " one when the TextChanged event fires
  141. call WaitFor( {-> pyxeval( 'ycm_state.FileParseRequestReady()' ) } )
  142. doautocmd TextChanged
  143. call WaitForAssert( {-> assert_true( empty( sign_getplaced(
  144. \ '%',
  145. \ { 'group': 'ycm_signs' } )[ 0 ][ 'signs' ] ) ) } )
  146. call assert_true( empty( getloclist( 0 ) ) )
  147. endfunction
  148. function! Test_MessagePoll_Multiple_Filetypes()
  149. call youcompleteme#test#setup#OpenFile(
  150. \ '/third_party/ycmd/ycmd/tests/java/testdata/simple_eclipse_project' .
  151. \ '/src/com/test/TestLauncher.java', {} )
  152. call WaitForAssert( {->
  153. \ assert_true( len( sign_getplaced(
  154. \ '%',
  155. \ { 'group': 'ycm_signs' } )[ 0 ][ 'signs' ] ) ) } )
  156. let java_signs = sign_getplaced(
  157. \ '%',
  158. \ { 'group': 'ycm_signs' } )[ 0 ][ 'signs' ]
  159. silent vsplit testdata/diagnostics/foo.cpp
  160. " Make sure we've left the java buffer
  161. call assert_equal( java_signs,
  162. \ sign_getplaced( '#', { 'group': 'ycm_signs' } )[ 0 ][ 'signs' ] )
  163. " Clangd emits two diagnostics for foo.cpp.
  164. call WaitForAssert( {->
  165. \ assert_equal(
  166. \ 2,
  167. \ len( sign_getplaced(
  168. \ '%',
  169. \ { 'group': 'ycm_signs' } )[ 0 ][ 'signs' ] ) ) } )
  170. let cpp_signs = sign_getplaced( '%',
  171. \ { 'group': 'ycm_signs' } )[ 0 ][ 'signs' ]
  172. call assert_false( java_signs == cpp_signs )
  173. endfunction
  174. function! Test_BufferWithoutAssociatedFile_HighlightingWorks()
  175. enew
  176. call setbufline( '%', 1, 'iiii' )
  177. setf c
  178. call WaitForAssert( {->
  179. \ assert_true( len( sign_getplaced(
  180. \ '%',
  181. \ { 'group': 'ycm_signs' } )[ 0 ][ 'signs' ] ) ) } )
  182. let expected_properties = [
  183. \ { 'id': 4,
  184. \ 'col': 1,
  185. \ 'end': 1,
  186. \ 'type': 'YcmErrorProperty',
  187. \ 'length': 0,
  188. \ 'start': 1 },
  189. \ { 'id': 3,
  190. \ 'col': 1,
  191. \ 'end': 1,
  192. \ 'type': 'YcmErrorProperty',
  193. \ 'length': 0,
  194. \ 'start': 1 },
  195. \ { 'id': 2,
  196. \ 'col': 1,
  197. \ 'end': 1,
  198. \ 'type': 'YcmErrorProperty',
  199. \ 'length': 4,
  200. \ 'start': 1 },
  201. \ { 'id': 1,
  202. \ 'col': 1,
  203. \ 'end': 1,
  204. \ 'type': 'YcmErrorProperty',
  205. \ 'length': 4,
  206. \ 'start': 1 } ]
  207. call CheckListOfDicts( prop_list( 1 ), expected_properties )
  208. endfunction
  209. function! Test_ThirdPartyDeletesItsTextProperty()
  210. enew
  211. call prop_type_add( 'ThirdPartyProperty', { 'highlight': 'Error' } )
  212. call prop_add( 1, 1, { 'type': 'ThirdPartyProperty', 'bufnr': bufnr('%'), 'id': 42 } )
  213. call prop_type_delete( 'ThirdPartyProperty' )
  214. py3 from ycm.vimsupport import GetTextProperties, GetIntValue
  215. call assert_equal( [], py3eval( 'GetTextProperties( GetIntValue( """bufnr( "%" )""" ) )' ) )
  216. endfunction
  217. function! Test_ShowDetailedDiagnostic_CmdLine()
  218. call youcompleteme#test#setup#OpenFile(
  219. \ '/test/testdata/cpp/fixit.cpp', {} )
  220. call cursor( [ 3, 1 ] )
  221. redir => output
  222. YcmShowDetailedDiagnostic
  223. redir END
  224. call assert_equal(
  225. \ "Format specifies type 'char *' but the argument has type 'int' "
  226. \ . '(fix available) [-Wformat]',
  227. \ trim( output ) )
  228. %bwipe!
  229. endfunction
  230. function! Test_ShowDetailedDiagnostic_PopupAtCursor()
  231. call youcompleteme#test#setup#OpenFile(
  232. \ '/test/testdata/cpp/fixit.cpp', {} )
  233. call cursor( [ 3, 1 ] )
  234. YcmShowDetailedDiagnostic popup
  235. let id = popup_locate( 4, 16 )
  236. call assert_notequal(
  237. \ 0,
  238. \ id,
  239. \ "Couldn't find popup! " .. youcompleteme#test#popup#DumpPopups() )
  240. if exists( '*popup_list' )
  241. let popups = popup_list()
  242. call assert_equal( 1, len( popups ) )
  243. endif
  244. call youcompleteme#test#popup#CheckPopupPosition( id, {
  245. \ 'visible': 1,
  246. \ 'col': 16,
  247. \ 'line': 4,
  248. \ } )
  249. call assert_equal(
  250. \ [
  251. \ "Format specifies type 'char *' but the argument has type 'int' "
  252. \ . '(fix available) [-Wformat]',
  253. \ ],
  254. \ getbufline( winbufnr(id), 1, '$' ) )
  255. " From vim's test_popupwin.vim
  256. " trigger the check for last_cursormoved by going into insert mode
  257. call test_override( 'char_avail', 1 )
  258. call feedkeys( "ji\<Esc>", 'xt' )
  259. call assert_equal( {}, popup_getpos( id ) )
  260. call test_override( 'ALL', 0 )
  261. %bwipe!
  262. endfunction
  263. function! Test_ShowDetailedDiagnostic_Popup_WithCharacters()
  264. let f = tempname() . '.cc'
  265. execut 'edit' f
  266. call setline( 1, [
  267. \ 'struct Foo {};',
  268. \ 'template<char...> Foo operator""_foo() { return {}; }',
  269. \ 'int main() {',
  270. \ '""_foo',
  271. \ '}',
  272. \ ] )
  273. call youcompleteme#test#setup#WaitForInitialParse( {} )
  274. call WaitForAssert( {->
  275. \ assert_true(
  276. \ py3eval(
  277. \ 'len( ycm_state.CurrentBuffer()._diag_interface._diagnostics )'
  278. \ ) ) } )
  279. call cursor( [ 4, 1 ] )
  280. YcmShowDetailedDiagnostic popup
  281. let id = popup_locate( 5, 7 )
  282. call assert_notequal(
  283. \ 0,
  284. \ id,
  285. \ "Couldn't find popup! " .. youcompleteme#test#popup#DumpPopups() )
  286. if exists( '*popup_list' )
  287. let popups = popup_list()
  288. call assert_equal( 1, len( popups ) )
  289. endif
  290. call youcompleteme#test#popup#CheckPopupPosition( id, {
  291. \ 'visible': 1,
  292. \ 'col': 7,
  293. \ 'line': 5,
  294. \ } )
  295. call assert_match(
  296. \ "^No matching literal operator for call to 'operator\"\"_foo'.*",
  297. \ getbufline( winbufnr(id), 1, '$' )[ 0 ] )
  298. " From vim's test_popupwin.vim
  299. " trigger the check for last_cursormoved by going into insert mode
  300. call test_override( 'char_avail', 1 )
  301. call feedkeys( "ji\<Esc>", 'xt' )
  302. call assert_equal( {}, popup_getpos( id ) )
  303. call test_override( 'ALL', 0 )
  304. %bwipe!
  305. endfunction
  306. function! Test_ShowDetailedDiagnostic_Popup_MultilineDiagNotFromStartOfLine()
  307. let f = tempname() . '.cc'
  308. execut 'edit' f
  309. call setline( 1, [
  310. \ 'int main () {',
  311. \ ' int a \',
  312. \ '=\',
  313. \ '=',
  314. \ '3;',
  315. \ '}',
  316. \ ] )
  317. call youcompleteme#test#setup#WaitForInitialParse( {} )
  318. call WaitForAssert( {->
  319. \ assert_true(
  320. \ py3eval(
  321. \ 'len( ycm_state.CurrentBuffer()._diag_interface._diagnostics )'
  322. \ ) ) } )
  323. call test_override( 'char_avail', 1 )
  324. for cursor_pos in [ [ 2, 9 ], [ 3, 1], [ 4, 1 ] ]
  325. call cursor( cursor_pos )
  326. YcmShowDetailedDiagnostic popup
  327. call assert_equal( len( popup_list() ), 1 )
  328. let id = popup_list()[ 0 ]
  329. call assert_notequal( 0, id, "Couldn't find popup!" )
  330. call assert_equal( [ 3, 10 ], win_screenpos( id ) )
  331. call youcompleteme#test#popup#CheckPopupPosition( id, {
  332. \ 'visible': 1,
  333. \ 'col': 10,
  334. \ 'line': 3,
  335. \ } )
  336. call assert_match(
  337. \ "^Invalid '==' at end of declaration; did you mean '='?.*",
  338. \ getbufline( winbufnr(id), 1, '$' )[ 0 ] )
  339. " From vim's test_popupwin.vim
  340. " trigger the check for last_cursormoved by going into insert mode
  341. call feedkeys( "ji\<Esc>", 'xt' )
  342. call assert_equal( {}, popup_getpos( id ) )
  343. endfor
  344. call test_override( 'ALL', 0 )
  345. %bwipe!
  346. endfunction
  347. function! Test_ShowDetailedDiagnostic_Popup_MultilineDiagFromStartOfLine()
  348. let f = tempname() . '.cc'
  349. execut 'edit' f
  350. call setline( 1, [
  351. \ 'int main () {',
  352. \ 'const int &&',
  353. \ ' /* */',
  354. \ ' rd = 1;',
  355. \ 'rd = 4;',
  356. \ '}',
  357. \ ] )
  358. call youcompleteme#test#setup#WaitForInitialParse( {} )
  359. call WaitForAssert( {->
  360. \ assert_true(
  361. \ py3eval(
  362. \ 'len( ycm_state.CurrentBuffer()._diag_interface._diagnostics )'
  363. \ ) ) } )
  364. call test_override( 'char_avail', 1 )
  365. for cursor_pos in [ [ 2, 1 ], [ 3, 9 ], [ 4, 5 ] ]
  366. call cursor( cursor_pos )
  367. YcmShowDetailedDiagnostic popup
  368. call assert_equal( 1, len( popup_list() ) )
  369. let id = popup_list()[ 0 ]
  370. call assert_notequal( 0, id, "Couldn't find popup!" )
  371. call assert_equal( [ 3, 13 ], win_screenpos( id ) )
  372. call youcompleteme#test#popup#CheckPopupPosition( id, {
  373. \ 'visible': 1,
  374. \ 'col': 13,
  375. \ 'line': 3,
  376. \ } )
  377. call assert_match(
  378. \ "^Variable 'rd' declared const here.*",
  379. \ getbufline( winbufnr(id), 1, '$' )[ 0 ] )
  380. " From vim's test_popupwin.vim
  381. " trigger the check for last_cursormoved by going into insert mode
  382. call feedkeys( "ji\<Esc>ki\<Esc>", 'xt' )
  383. call assert_equal( {}, popup_getpos( id ) )
  384. endfor
  385. call test_override( 'ALL', 0 )
  386. %bwipe!
  387. endfunction
  388. function! Test_ShowDetailedDiagnostic_Popup_MultipleDiagsPerLine_SameMessage()
  389. let f = tempname() . '.cc'
  390. execut 'edit' f
  391. call setline( 1, [ 'void f(){a;a;}', ] )
  392. call youcompleteme#test#setup#WaitForInitialParse( {} )
  393. call WaitForAssert( {->
  394. \ assert_true(
  395. \ py3eval(
  396. \ 'len( ycm_state.CurrentBuffer()._diag_interface._diagnostics )'
  397. \ ) ) } )
  398. YcmShowDetailedDiagnostic popup
  399. let popup_list = popup_list()
  400. call assert_equal( 1, len( popup_list ) )
  401. call popup_close( popup_list[ 0 ] )
  402. endfunction