1# Copyright (C) 2013-2020 ycmd contributors 2# 3# This file is part of ycmd. 4# 5# ycmd is free software: you can redistribute it and/or modify 6# it under the terms of the GNU General Public License as published by 7# the Free Software Foundation, either version 3 of the License, or 8# (at your option) any later version. 9# 10# ycmd is distributed in the hope that it will be useful, 11# but WITHOUT ANY WARRANTY; without even the implied warranty of 12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13# GNU General Public License for more details. 14# 15# You should have received a copy of the GNU General Public License 16# along with ycmd. If not, see <http://www.gnu.org/licenses/>. 17 18from hamcrest import ( assert_that, 19 contains_exactly, 20 contains_inanyorder, 21 empty, 22 equal_to, 23 has_entries, 24 has_items ) 25from unittest.mock import patch 26from ycmd.tests import IsolatedYcmd, SharedYcmd, PathToTestFile 27from ycmd.tests.test_utils import ( BuildRequest, CompletionEntryMatcher, 28 DummyCompleter, PatchCompleter ) 29 30 31@SharedYcmd 32def GetCompletions_RequestValidation_NoLineNumException_test( app ): 33 response = app.post_json( '/semantic_completion_available', { 34 'column_num': 0, 35 'filepath': '/foo', 36 'file_data': { 37 '/foo': { 38 'filetypes': [ 'text' ], 39 'contents': 'zoo' 40 } 41 } 42 }, status = '5*', expect_errors = True ) 43 response.mustcontain( 'missing', 'line_num' ) 44 45 46@SharedYcmd 47def GetCompletions_IdentifierCompleter_Works_test( app ): 48 event_data = BuildRequest( contents = 'foo foogoo ba', 49 event_name = 'FileReadyToParse' ) 50 51 app.post_json( '/event_notification', event_data ) 52 53 # query is 'oo' 54 completion_data = BuildRequest( contents = 'oo foo foogoo ba', 55 column_num = 3 ) 56 response_data = app.post_json( '/completions', completion_data ).json 57 58 assert_that( 1, equal_to( response_data[ 'completion_start_column' ] ) ) 59 assert_that( 60 response_data[ 'completions' ], 61 has_items( CompletionEntryMatcher( 'foo', '[ID]' ), 62 CompletionEntryMatcher( 'foogoo', '[ID]' ) ) 63 ) 64 65 66@IsolatedYcmd( { 'min_num_identifier_candidate_chars': 4 } ) 67def GetCompletions_IdentifierCompleter_FilterShortCandidates_test( app ): 68 event_data = BuildRequest( contents = 'foo foogoo gooo', 69 event_name = 'FileReadyToParse' ) 70 app.post_json( '/event_notification', event_data ) 71 72 completion_data = BuildRequest( contents = 'oo', column_num = 3 ) 73 response = app.post_json( '/completions', 74 completion_data ).json[ 'completions' ] 75 76 assert_that( response, 77 contains_inanyorder( CompletionEntryMatcher( 'foogoo' ), 78 CompletionEntryMatcher( 'gooo' ) ) ) 79 80 81@SharedYcmd 82def GetCompletions_IdentifierCompleter_StartColumn_AfterWord_test( app ): 83 completion_data = BuildRequest( contents = 'oo foo foogoo ba', 84 column_num = 11 ) 85 response_data = app.post_json( '/completions', completion_data ).json 86 assert_that( 8, equal_to( response_data[ 'completion_start_column' ] ) ) 87 88 89@SharedYcmd 90def GetCompletions_IdentifierCompleter_WorksForSpecialIdentifierChars_test( 91 app ): 92 contents = """ 93 textarea { 94 font-family: sans-serif; 95 font-size: 12px; 96 }""" 97 event_data = BuildRequest( contents = contents, 98 filetype = 'css', 99 event_name = 'FileReadyToParse' ) 100 101 app.post_json( '/event_notification', event_data ) 102 103 # query is 'fo' 104 completion_data = BuildRequest( contents = 'fo ' + contents, 105 filetype = 'css', 106 column_num = 3 ) 107 results = app.post_json( '/completions', 108 completion_data ).json[ 'completions' ] 109 110 assert_that( 111 results, 112 has_items( CompletionEntryMatcher( 'font-size', '[ID]' ), 113 CompletionEntryMatcher( 'font-family', '[ID]' ) ) 114 ) 115 116 117@SharedYcmd 118def GetCompletions_IdentifierCompleter_Unicode_InLine_test( app ): 119 contents = """ 120 This is some text cøntaining unicøde 121 """ 122 123 event_data = BuildRequest( contents = contents, 124 filetype = 'css', 125 event_name = 'FileReadyToParse' ) 126 127 app.post_json( '/event_notification', event_data ) 128 129 # query is 'tx' 130 completion_data = BuildRequest( contents = 'tx ' + contents, 131 filetype = 'css', 132 column_num = 3 ) 133 results = app.post_json( '/completions', 134 completion_data ).json[ 'completions' ] 135 136 assert_that( 137 results, 138 has_items( CompletionEntryMatcher( 'text', '[ID]' ) ) 139 ) 140 141 142@SharedYcmd 143def GetCompletions_IdentifierCompleter_UnicodeQuery_InLine_test( app ): 144 contents = """ 145 This is some text cøntaining unicøde 146 """ 147 148 event_data = BuildRequest( contents = contents, 149 filetype = 'css', 150 event_name = 'FileReadyToParse' ) 151 152 app.post_json( '/event_notification', event_data ) 153 154 # query is 'cø' 155 completion_data = BuildRequest( contents = 'cø ' + contents, 156 filetype = 'css', 157 column_num = 4 ) 158 results = app.post_json( '/completions', 159 completion_data ).json[ 'completions' ] 160 161 assert_that( 162 results, 163 has_items( CompletionEntryMatcher( 'cøntaining', '[ID]' ), 164 CompletionEntryMatcher( 'unicøde', '[ID]' ) ) 165 ) 166 167 168@IsolatedYcmd() 169def GetCompletions_IdentifierCompleter_Unicode_MultipleCodePoints_test( app ): 170 # The first ō is on one code point while the second is on two ("o" + combining 171 # macron character). 172 event_data = BuildRequest( contents = 'fōo\nfōo', 173 event_name = 'FileReadyToParse' ) 174 175 app.post_json( '/event_notification', event_data ) 176 177 # query is 'fo' 178 completion_data = BuildRequest( contents = 'fōo\nfōo\nfo', 179 line_num = 3, 180 column_num = 3 ) 181 response_data = app.post_json( '/completions', completion_data ).json 182 183 assert_that( 1, equal_to( response_data[ 'completion_start_column' ] ) ) 184 assert_that( 185 response_data[ 'completions' ], 186 has_items( CompletionEntryMatcher( 'fōo', '[ID]' ), 187 CompletionEntryMatcher( 'fōo', '[ID]' ) ) 188 ) 189 190 191@SharedYcmd 192@patch( 'ycmd.tests.test_utils.DummyCompleter.CandidatesList', 193 return_value = [ 'foo', 'bar', 'qux' ] ) 194def GetCompletions_ForceSemantic_Works_test( candidates, app ): 195 with PatchCompleter( DummyCompleter, 'dummy_filetype' ): 196 completion_data = BuildRequest( filetype = 'dummy_filetype', 197 force_semantic = True ) 198 199 results = app.post_json( '/completions', 200 completion_data ).json[ 'completions' ] 201 assert_that( results, has_items( CompletionEntryMatcher( 'foo' ), 202 CompletionEntryMatcher( 'bar' ), 203 CompletionEntryMatcher( 'qux' ) ) ) 204 205 206@SharedYcmd 207def GetCompletions_ForceSemantic_NoSemanticCompleter_test( app, *args ): 208 event_data = BuildRequest( event_name = 'FileReadyToParse', 209 filetype = 'dummy_filetype', 210 contents = 'complete_this_word\ncom' ) 211 app.post_json( '/event_notification', event_data ) 212 213 completion_data = BuildRequest( filetype = 'dummy_filetype', 214 force_semantic = True, 215 contents = 'complete_this_word\ncom', 216 line_number = 2, 217 column_num = 4 ) 218 results = app.post_json( '/completions', completion_data ).json 219 assert_that( results, has_entries( { 220 'completions': empty(), 221 'errors': empty(), 222 } ) ) 223 224 # For proof, show that non-forced completion would return identifiers 225 completion_data = BuildRequest( filetype = 'dummy_filetype', 226 contents = 'complete_this_word\ncom', 227 line_number = 2, 228 column_num = 4 ) 229 results = app.post_json( '/completions', completion_data ).json 230 assert_that( results, has_entries( { 231 'completions': contains_exactly( 232 CompletionEntryMatcher( 'com' ), 233 CompletionEntryMatcher( 'complete_this_word' ) ), 234 'errors': empty(), 235 } ) ) 236 237 238@SharedYcmd 239def GetCompletions_IdentifierCompleter_SyntaxKeywordsAdded_test( app ): 240 event_data = BuildRequest( event_name = 'FileReadyToParse', 241 syntax_keywords = [ 'foo', 'bar', 'zoo' ] ) 242 243 app.post_json( '/event_notification', event_data ) 244 245 completion_data = BuildRequest( contents = 'oo ', 246 column_num = 3 ) 247 248 results = app.post_json( '/completions', 249 completion_data ).json[ 'completions' ] 250 assert_that( results, 251 has_items( CompletionEntryMatcher( 'foo' ), 252 CompletionEntryMatcher( 'zoo' ) ) ) 253 254 255@SharedYcmd 256def GetCompletions_IdentifierCompleter_TagsAdded_test( app ): 257 event_data = BuildRequest( event_name = 'FileReadyToParse', 258 tag_files = [ PathToTestFile( 'basic.tags' ) ] ) 259 app.post_json( '/event_notification', event_data ) 260 261 completion_data = BuildRequest( contents = 'oo', 262 column_num = 3, 263 filetype = 'cpp' ) 264 results = app.post_json( '/completions', 265 completion_data ).json[ 'completions' ] 266 assert_that( results, 267 has_items( CompletionEntryMatcher( 'foosy' ), 268 CompletionEntryMatcher( 'fooaaa' ) ) ) 269 270 271@SharedYcmd 272def GetCompletions_IdentifierCompleter_JustFinishedIdentifier_test( app ): 273 event_data = BuildRequest( event_name = 'CurrentIdentifierFinished', 274 column_num = 4, 275 contents = 'foo' ) 276 app.post_json( '/event_notification', event_data ) 277 278 completion_data = BuildRequest( contents = 'oo', column_num = 3 ) 279 results = app.post_json( '/completions', 280 completion_data ).json[ 'completions' ] 281 assert_that( results, 282 has_items( CompletionEntryMatcher( 'foo' ) ) ) 283 284 285@IsolatedYcmd() 286def GetCompletions_IdentifierCompleter_IgnoreFinishedIdentifierInString_test( 287 app ): 288 289 event_data = BuildRequest( event_name = 'CurrentIdentifierFinished', 290 column_num = 6, 291 contents = '"foo"' ) 292 293 app.post_json( '/event_notification', event_data ) 294 295 completion_data = BuildRequest( contents = 'oo', column_num = 3 ) 296 results = app.post_json( '/completions', 297 completion_data ).json[ 'completions' ] 298 assert_that( results, empty() ) 299 300 301@SharedYcmd 302def GetCompletions_IdentifierCompleter_IdentifierUnderCursor_test( app ): 303 event_data = BuildRequest( event_name = 'InsertLeave', 304 column_num = 2, 305 contents = 'foo' ) 306 app.post_json( '/event_notification', event_data ) 307 308 completion_data = BuildRequest( contents = 'oo', column_num = 3 ) 309 results = app.post_json( '/completions', 310 completion_data ).json[ 'completions' ] 311 assert_that( results, 312 has_items( CompletionEntryMatcher( 'foo' ) ) ) 313 314 315@IsolatedYcmd() 316def GetCompletions_IdentifierCompleter_IgnoreCursorIdentifierInString_test( 317 app ): 318 319 event_data = BuildRequest( event_name = 'InsertLeave', 320 column_num = 3, 321 contents = '"foo"' ) 322 app.post_json( '/event_notification', event_data ) 323 324 completion_data = BuildRequest( contents = 'oo', column_num = 3 ) 325 results = app.post_json( '/completions', 326 completion_data ).json[ 'completions' ] 327 assert_that( results, empty() ) 328 329 330@SharedYcmd 331def GetCompletions_FilenameCompleter_Works_test( app ): 332 filepath = PathToTestFile( 'filename_completer', 'test.foo' ) 333 completion_data = BuildRequest( filepath = filepath, 334 contents = './', 335 column_num = 3 ) 336 results = app.post_json( '/completions', 337 completion_data ).json[ 'completions' ] 338 assert_that( results, 339 has_items( CompletionEntryMatcher( 'inner_dir', '[Dir]' ) ) ) 340 341 342@SharedYcmd 343def GetCompletions_FilenameCompleter_FallBackToIdentifierCompleter_test( app ): 344 filepath = PathToTestFile( 'filename_completer', 'test.foo' ) 345 event_data = BuildRequest( filepath = filepath, 346 contents = './nonexisting_dir', 347 filetype = 'foo', 348 event_name = 'FileReadyToParse' ) 349 350 app.post_json( '/event_notification', event_data ) 351 352 completion_data = BuildRequest( filepath = filepath, 353 contents = './nonexisting_dir nd', 354 filetype = 'foo', 355 column_num = 21 ) 356 357 assert_that( 358 app.post_json( '/completions', completion_data ).json[ 'completions' ], 359 has_items( CompletionEntryMatcher( 'nonexisting_dir', '[ID]' ) ) 360 ) 361 362 363@SharedYcmd 364def GetCompletions_UltiSnipsCompleter_Works_test( app ): 365 event_data = BuildRequest( 366 event_name = 'BufferVisit', 367 ultisnips_snippets = [ 368 { 'trigger': 'foo', 'description': 'bar' }, 369 { 'trigger': 'zoo', 'description': 'goo' }, 370 ] ) 371 372 app.post_json( '/event_notification', event_data ) 373 374 completion_data = BuildRequest( contents = 'oo ', 375 column_num = 3 ) 376 377 results = app.post_json( '/completions', 378 completion_data ).json[ 'completions' ] 379 assert_that( 380 results, 381 has_items( 382 CompletionEntryMatcher( 'foo', extra_menu_info='<snip> bar' ), 383 CompletionEntryMatcher( 'zoo', extra_menu_info='<snip> goo' ) 384 ) 385 ) 386 387 388@IsolatedYcmd( { 'use_ultisnips_completer': 0 } ) 389def GetCompletions_UltiSnipsCompleter_UnusedWhenOffWithOption_test( app ): 390 event_data = BuildRequest( 391 event_name = 'BufferVisit', 392 ultisnips_snippets = [ 393 { 'trigger': 'foo', 'description': 'bar' }, 394 { 'trigger': 'zoo', 'description': 'goo' }, 395 ] ) 396 397 app.post_json( '/event_notification', event_data ) 398 399 completion_data = BuildRequest( contents = 'oo ', column_num = 3 ) 400 401 assert_that( app.post_json( '/completions', completion_data ).json, 402 has_entries( { 'completions': empty() } ) ) 403 404 405@IsolatedYcmd( { 'semantic_triggers': { 'dummy_filetype': [ '_' ] } } ) 406@patch( 'ycmd.tests.test_utils.DummyCompleter.CandidatesList', 407 return_value = [ 'some_candidate' ] ) 408def GetCompletions_SemanticCompleter_WorksWhenTriggerIsIdentifier_test( 409 candidates, app ): 410 with PatchCompleter( DummyCompleter, 'dummy_filetype' ): 411 completion_data = BuildRequest( filetype = 'dummy_filetype', 412 contents = 'some_can', 413 column_num = 9 ) 414 415 results = app.post_json( '/completions', 416 completion_data ).json[ 'completions' ] 417 assert_that( 418 results, 419 has_items( CompletionEntryMatcher( 'some_candidate' ) ) 420 ) 421 422 423@SharedYcmd 424@patch( 'ycmd.tests.test_utils.DummyCompleter.ShouldUseNowInner', 425 return_value = True ) 426@patch( 'ycmd.tests.test_utils.DummyCompleter.CandidatesList', 427 return_value = [ 'attribute' ] ) 428def GetCompletions_CacheIsValid_test( 429 candidates_list, should_use, app ): 430 with PatchCompleter( DummyCompleter, 'dummy_filetype' ): 431 completion_data = BuildRequest( filetype = 'dummy_filetype', 432 contents = 'object.attr', 433 line_num = 1, 434 column_num = 12 ) 435 436 results = app.post_json( '/completions', 437 completion_data ).json[ 'completions' ] 438 assert_that( 439 results, 440 has_items( CompletionEntryMatcher( 'attribute' ) ) 441 ) 442 443 completion_data = BuildRequest( filetype = 'dummy_filetype', 444 contents = 'object.attri', 445 line_num = 1, 446 column_num = 13 ) 447 448 results = app.post_json( '/completions', 449 completion_data ).json[ 'completions' ] 450 assert_that( 451 results, 452 has_items( CompletionEntryMatcher( 'attribute' ) ) 453 ) 454 455 # We ask for candidates only once because of cache. 456 assert_that( candidates_list.call_count, equal_to( 1 ) ) 457 458 459@SharedYcmd 460@patch( 'ycmd.tests.test_utils.DummyCompleter.ShouldUseNowInner', 461 return_value = True ) 462@patch( 'ycmd.tests.test_utils.DummyCompleter.CandidatesList', 463 side_effect = [ [ 'attributeA' ], [ 'attributeB' ] ] ) 464def GetCompletions_CacheIsNotValid_DifferentLineNumber_test( 465 candidates_list, should_use, app ): 466 with PatchCompleter( DummyCompleter, 'dummy_filetype' ): 467 completion_data = BuildRequest( filetype = 'dummy_filetype', 468 contents = 'objectA.attr\n' 469 'objectB.attr', 470 line_num = 1, 471 column_num = 12 ) 472 473 results = app.post_json( '/completions', 474 completion_data ).json[ 'completions' ] 475 assert_that( 476 results, 477 has_items( CompletionEntryMatcher( 'attributeA' ) ) 478 ) 479 480 completion_data = BuildRequest( filetype = 'dummy_filetype', 481 contents = 'objectA.\n' 482 'objectB.', 483 line_num = 2, 484 column_num = 12 ) 485 486 results = app.post_json( '/completions', 487 completion_data ).json[ 'completions' ] 488 assert_that( 489 results, 490 has_items( CompletionEntryMatcher( 'attributeB' ) ) 491 ) 492 493 # We ask for candidates twice because of cache invalidation: 494 # line numbers are different between requests. 495 assert_that( candidates_list.call_count, equal_to( 2 ) ) 496 497 498@SharedYcmd 499@patch( 'ycmd.tests.test_utils.DummyCompleter.ShouldUseNowInner', 500 return_value = True ) 501@patch( 'ycmd.tests.test_utils.DummyCompleter.CandidatesList', 502 side_effect = [ [ 'attributeA' ], [ 'attributeB' ] ] ) 503def GetCompletions_CacheIsNotValid_DifferentStartColumn_test( 504 candidates_list, should_use, app ): 505 with PatchCompleter( DummyCompleter, 'dummy_filetype' ): 506 # Start column is 9 507 completion_data = BuildRequest( filetype = 'dummy_filetype', 508 contents = 'objectA.attr', 509 line_num = 1, 510 column_num = 12 ) 511 512 results = app.post_json( '/completions', 513 completion_data ).json[ 'completions' ] 514 assert_that( 515 results, 516 has_items( CompletionEntryMatcher( 'attributeA' ) ) 517 ) 518 519 # Start column is 8 520 completion_data = BuildRequest( filetype = 'dummy_filetype', 521 contents = 'object.attri', 522 line_num = 1, 523 column_num = 12 ) 524 525 results = app.post_json( '/completions', 526 completion_data ).json[ 'completions' ] 527 assert_that( 528 results, 529 has_items( CompletionEntryMatcher( 'attributeB' ) ) 530 ) 531 532 # We ask for candidates twice because of cache invalidation: 533 # start columns are different between requests. 534 assert_that( candidates_list.call_count, equal_to( 2 ) ) 535 536 537@SharedYcmd 538@patch( 'ycmd.tests.test_utils.DummyCompleter.ShouldUseNowInner', 539 return_value = True ) 540@patch( 'ycmd.tests.test_utils.DummyCompleter.CandidatesList', 541 side_effect = [ [ 'attributeA' ], [ 'attributeB' ] ] ) 542def GetCompletions_CacheIsNotValid_DifferentForceSemantic_test( 543 candidates_list, should_use, app ): 544 with PatchCompleter( DummyCompleter, 'dummy_filetype' ): 545 completion_data = BuildRequest( filetype = 'dummy_filetype', 546 contents = 'objectA.attr', 547 line_num = 1, 548 column_num = 12, 549 force_semantic = True ) 550 551 results = app.post_json( '/completions', 552 completion_data ).json[ 'completions' ] 553 assert_that( 554 results, 555 has_items( CompletionEntryMatcher( 'attributeA' ) ) 556 ) 557 558 completion_data = BuildRequest( filetype = 'dummy_filetype', 559 contents = 'objectA.attr', 560 line_num = 1, 561 column_num = 12 ) 562 563 results = app.post_json( '/completions', 564 completion_data ).json[ 'completions' ] 565 assert_that( 566 results, 567 has_items( CompletionEntryMatcher( 'attributeB' ) ) 568 ) 569 570 # We ask for candidates twice because of cache invalidation: 571 # semantic completion is forced for one of the request, not the other. 572 assert_that( candidates_list.call_count, equal_to( 2 ) ) 573 574 575@SharedYcmd 576@patch( 'ycmd.tests.test_utils.DummyCompleter.ShouldUseNowInner', 577 return_value = True ) 578@patch( 'ycmd.tests.test_utils.DummyCompleter.CandidatesList', 579 side_effect = [ [ 'attributeA' ], [ 'attributeB' ] ] ) 580def GetCompletions_CacheIsNotValid_DifferentContents_test( 581 candidates_list, should_use, app ): 582 with PatchCompleter( DummyCompleter, 'dummy_filetype' ): 583 completion_data = BuildRequest( filetype = 'dummy_filetype', 584 contents = 'objectA = foo\n' 585 'objectA.attr', 586 line_num = 2, 587 column_num = 12 ) 588 589 results = app.post_json( '/completions', 590 completion_data ).json[ 'completions' ] 591 assert_that( 592 results, 593 has_items( CompletionEntryMatcher( 'attributeA' ) ) 594 ) 595 596 completion_data = BuildRequest( filetype = 'dummy_filetype', 597 contents = 'objectA = bar\n' 598 'objectA.attr', 599 line_num = 2, 600 column_num = 12 ) 601 602 results = app.post_json( '/completions', 603 completion_data ).json[ 'completions' ] 604 assert_that( 605 results, 606 has_items( CompletionEntryMatcher( 'attributeB' ) ) 607 ) 608 609 # We ask for candidates twice because of cache invalidation: 610 # both requests have the same cursor position and current line but file 611 # contents are different. 612 assert_that( candidates_list.call_count, equal_to( 2 ) ) 613 614 615@SharedYcmd 616@patch( 'ycmd.tests.test_utils.DummyCompleter.ShouldUseNowInner', 617 return_value = True ) 618@patch( 'ycmd.tests.test_utils.DummyCompleter.CandidatesList', 619 side_effect = [ [ 'attributeA' ], [ 'attributeB' ] ] ) 620def GetCompletions_CacheIsNotValid_DifferentNumberOfLines_test( 621 candidates_list, should_use, app ): 622 with PatchCompleter( DummyCompleter, 'dummy_filetype' ): 623 completion_data = BuildRequest( filetype = 'dummy_filetype', 624 contents = 'objectA.attr\n' 625 'objectB.attr', 626 line_num = 1, 627 column_num = 12 ) 628 629 results = app.post_json( '/completions', 630 completion_data ).json[ 'completions' ] 631 assert_that( 632 results, 633 has_items( CompletionEntryMatcher( 'attributeA' ) ) 634 ) 635 636 completion_data = BuildRequest( filetype = 'dummy_filetype', 637 contents = 'objectA.attr', 638 line_num = 1, 639 column_num = 12 ) 640 641 results = app.post_json( '/completions', 642 completion_data ).json[ 'completions' ] 643 assert_that( 644 results, 645 has_items( CompletionEntryMatcher( 'attributeB' ) ) 646 ) 647 648 # We ask for candidates twice because of cache invalidation: 649 # both requests have the same cursor position and current line but the 650 # number of lines in the current file is different. 651 assert_that( candidates_list.call_count, equal_to( 2 ) ) 652 653 654@SharedYcmd 655@patch( 'ycmd.tests.test_utils.DummyCompleter.ShouldUseNowInner', 656 return_value = True ) 657@patch( 'ycmd.tests.test_utils.DummyCompleter.CandidatesList', 658 side_effect = [ [ 'attributeA' ], [ 'attributeB' ] ] ) 659def GetCompletions_CacheIsNotValid_DifferentFileData_test( 660 candidates_list, should_use, app ): 661 with PatchCompleter( DummyCompleter, 'dummy_filetype' ): 662 completion_data = BuildRequest( filetype = 'dummy_filetype', 663 contents = 'objectA.attr', 664 line_num = 1, 665 column_num = 12, 666 file_data = { 667 '/bar': { 668 'contents': 'objectA = foo', 669 'filetypes': [ 'dummy_filetype' ] 670 } 671 } ) 672 673 results = app.post_json( '/completions', 674 completion_data ).json[ 'completions' ] 675 assert_that( 676 results, 677 has_items( CompletionEntryMatcher( 'attributeA' ) ) 678 ) 679 680 completion_data = BuildRequest( filetype = 'dummy_filetype', 681 contents = 'objectA.attr', 682 line_num = 1, 683 column_num = 12, 684 file_data = { 685 '/bar': { 686 'contents': 'objectA = bar', 687 'filetypes': [ 'dummy_filetype' ] 688 } 689 } ) 690 691 results = app.post_json( '/completions', 692 completion_data ).json[ 'completions' ] 693 assert_that( 694 results, 695 has_items( CompletionEntryMatcher( 'attributeB' ) ) 696 ) 697 698 # We ask for candidates twice because of cache invalidation: 699 # both requests have the same cursor position and contents for the current 700 # file but different contents for another file. 701 assert_that( candidates_list.call_count, equal_to( 2 ) ) 702 703 704@SharedYcmd 705@patch( 'ycmd.tests.test_utils.DummyCompleter.ShouldUseNowInner', 706 return_value = True ) 707@patch( 'ycmd.tests.test_utils.DummyCompleter.CandidatesList', 708 side_effect = [ [ 'attributeA' ], [ 'attributeB' ] ] ) 709def GetCompletions_CacheIsNotValid_DifferentExtraConfData_test( 710 candidates_list, should_use, app ): 711 with PatchCompleter( DummyCompleter, 'dummy_filetype' ): 712 completion_data = BuildRequest( filetype = 'dummy_filetype', 713 contents = 'objectA.attr', 714 line_num = 1, 715 column_num = 12 ) 716 717 results = app.post_json( '/completions', 718 completion_data ).json[ 'completions' ] 719 assert_that( 720 results, 721 has_items( CompletionEntryMatcher( 'attributeA' ) ) 722 ) 723 724 completion_data = BuildRequest( filetype = 'dummy_filetype', 725 contents = 'objectA.attr', 726 line_num = 1, 727 column_num = 12, 728 extra_conf_data = { 'key': 'value' } ) 729 730 results = app.post_json( '/completions', 731 completion_data ).json[ 'completions' ] 732 assert_that( 733 results, 734 has_items( CompletionEntryMatcher( 'attributeB' ) ) 735 ) 736 737 # We ask for candidates twice because of cache invalidation: 738 # both requests are identical except the extra conf data. 739 assert_that( candidates_list.call_count, equal_to( 2 ) ) 740 741 742@SharedYcmd 743@patch( 'ycmd.tests.test_utils.DummyCompleter.ShouldUseNowInner', 744 return_value = True ) 745@patch( 'ycmd.tests.test_utils.DummyCompleter.CandidatesList', 746 return_value = [ 'aba', 'cbc' ] ) 747def GetCompletions_FilterThenReturnFromCache_test( candidates_list, 748 should_use, 749 app ): 750 751 with PatchCompleter( DummyCompleter, 'dummy_filetype' ): 752 # First, fill the cache with an empty query 753 completion_data = BuildRequest( filetype = 'dummy_filetype', 754 contents = 'objectA.', 755 line_num = 1, 756 column_num = 9 ) 757 758 results = app.post_json( '/completions', 759 completion_data ).json[ 'completions' ] 760 assert_that( results, 761 has_items( CompletionEntryMatcher( 'aba' ), 762 CompletionEntryMatcher( 'cbc' ) ) ) 763 764 # Now, filter them. This causes them to be converted to bytes and back 765 completion_data = BuildRequest( filetype = 'dummy_filetype', 766 contents = 'objectA.c', 767 line_num = 1, 768 column_num = 10 ) 769 770 results = app.post_json( '/completions', 771 completion_data ).json[ 'completions' ] 772 assert_that( results, 773 has_items( CompletionEntryMatcher( 'cbc' ) ) ) 774 775 # Finally, request the original (unfiltered) set again. Ensure that we get 776 # proper results (not some bytes objects) 777 completion_data = BuildRequest( filetype = 'dummy_filetype', 778 contents = 'objectA.', 779 line_num = 1, 780 column_num = 9 ) 781 782 results = app.post_json( '/completions', 783 completion_data ).json[ 'completions' ] 784 assert_that( results, 785 has_items( CompletionEntryMatcher( 'aba' ), 786 CompletionEntryMatcher( 'cbc' ) ) ) 787 788 assert_that( candidates_list.call_count, equal_to( 1 ) ) 789 790 791def Dummy_test(): 792 # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 793 assert True 794