1# Copyright (C) 2017-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 18import time 19from hamcrest import ( assert_that, 20 contains_exactly, 21 contains_inanyorder, 22 empty, 23 equal_to, 24 has_entries, 25 has_entry, 26 instance_of, 27 is_not, 28 matches_regexp ) 29from pprint import pformat 30import requests 31import pytest 32import json 33 34from ycmd.utils import ReadFile 35from ycmd.completers.java.java_completer import NO_DOCUMENTATION_MESSAGE 36from ycmd.tests.java import ( PathToTestFile, 37 SharedYcmd, 38 StartJavaCompleterServerWithFile, 39 IsolatedYcmd ) 40from ycmd.tests.test_utils import ( BuildRequest, 41 ChunkMatcher, 42 CombineRequest, 43 ErrorMatcher, 44 ExpectedFailure, 45 LocationMatcher, 46 WithRetry ) 47from unittest.mock import patch 48from ycmd import handlers 49from ycmd.completers.language_server import language_server_protocol as lsp 50from ycmd.completers.language_server.language_server_completer import ( 51 ResponseTimeoutException, 52 ResponseFailedException 53) 54from ycmd.responses import UnknownExtraConf 55 56TESTLAUNCHER_JAVA = PathToTestFile( 'simple_eclipse_project', 57 'src', 58 'com', 59 'test', 60 'TestLauncher.java' ) 61 62TEST_JAVA = PathToTestFile( 'simple_eclipse_project', 63 'src', 64 'com', 65 'youcompleteme', 66 'Test.java' ) 67 68TSET_JAVA = PathToTestFile( 'simple_eclipse_project', 69 'src', 70 'com', 71 'youcompleteme', 72 'testing', 73 'Tset.java' ) 74 75 76@WithRetry 77@SharedYcmd 78def Subcommands_DefinedSubcommands_test( app ): 79 subcommands_data = BuildRequest( completer_target = 'java' ) 80 81 assert_that( app.post_json( '/defined_subcommands', subcommands_data ).json, 82 contains_inanyorder( 83 'FixIt', 84 'ExecuteCommand', 85 'Format', 86 'GoToDeclaration', 87 'GoToDefinition', 88 'GoToDocumentOutline', 89 'GoTo', 90 'GetDoc', 91 'GetType', 92 'GoToImplementation', 93 'GoToReferences', 94 'GoToType', 95 'GoToSymbol', 96 'OpenProject', 97 'OrganizeImports', 98 'RefactorRename', 99 'RestartServer', 100 'WipeWorkspace' ) ) 101 102 103@pytest.mark.parametrize( 'cmd,arguments', [ 104 ( 'GoTo', [] ), 105 ( 'GoToDeclaration', [] ), 106 ( 'GoToDefinition', [] ), 107 ( 'GoToReferences', [] ), 108 ( 'GoToDocumentOutline', [] ), 109 ( 'GoToSymbol', [ 'test' ] ), 110 ( 'GetType', [] ), 111 ( 'GetDoc', [] ), 112 ( 'FixIt', [] ), 113 ( 'Format', [] ), 114 ( 'OrganizeImports', [] ), 115 ( 'RefactorRename', [ 'test' ] ), 116] ) 117@SharedYcmd 118def Subcommands_ServerNotInitialized_test( app, cmd, arguments ): 119 filepath = PathToTestFile( 'simple_eclipse_project', 120 'src', 121 'com', 122 'test', 123 'AbstractTestWidget.java' ) 124 125 completer = handlers._server_state.GetFiletypeCompleter( [ 'java' ] ) 126 127 @patch.object( completer, '_ServerIsInitialized', return_value = False ) 128 def Test( app, cmd, arguments, *args ): 129 RunTest( app, { 130 'description': 'Subcommand ' + cmd + ' handles server not ready', 131 'request': { 132 'command': cmd, 133 'line_num': 1, 134 'column_num': 1, 135 'filepath': filepath, 136 'arguments': arguments, 137 }, 138 'expect': { 139 'response': requests.codes.internal_server_error, 140 'data': ErrorMatcher( RuntimeError, 141 'Server is initializing. Please wait.' ), 142 } 143 } ) 144 145 Test( app, cmd, arguments ) 146 147 148def RunTest( app, test, contents = None ): 149 if not contents: 150 contents = ReadFile( test[ 'request' ][ 'filepath' ] ) 151 152 # Because we aren't testing this command, we *always* ignore errors. This 153 # is mainly because we (may) want to test scenarios where the completer 154 # throws an exception. 155 app.post_json( '/event_notification', 156 CombineRequest( test[ 'request' ], { 157 'event_name': 'FileReadyToParse', 158 'contents': contents, 159 'filetype': 'java', 160 } ), 161 expect_errors = True ) 162 163 # We also ignore errors here, but then we check the response code 164 # ourself. This is to allow testing of requests returning errors. 165 expiry = time.time() + 10 166 while True: 167 try: 168 response = app.post_json( 169 '/run_completer_command', 170 CombineRequest( test[ 'request' ], { 171 'completer_target': 'filetype_default', 172 'contents': contents, 173 'filetype': 'java', 174 'command_arguments': ( [ test[ 'request' ][ 'command' ] ] 175 + test[ 'request' ].get( 'arguments', [] ) ) 176 } ), 177 expect_errors = True 178 ) 179 180 print( f'completer response: { pformat( response.json ) }' ) 181 182 assert_that( response.status_code, 183 equal_to( test[ 'expect' ][ 'response' ] ) ) 184 185 assert_that( response.json, test[ 'expect' ][ 'data' ] ) 186 break 187 except AssertionError: 188 if time.time() > expiry: 189 raise 190 191 time.sleep( 0.25 ) 192 193 194@WithRetry 195@SharedYcmd 196def Subcommands_GetDoc_NoDoc_test( app ): 197 filepath = PathToTestFile( 'simple_eclipse_project', 198 'src', 199 'com', 200 'test', 201 'AbstractTestWidget.java' ) 202 contents = ReadFile( filepath ) 203 204 event_data = BuildRequest( filepath = filepath, 205 filetype = 'java', 206 line_num = 18, 207 column_num = 1, 208 contents = contents, 209 command_arguments = [ 'GetDoc' ], 210 completer_target = 'filetype_default' ) 211 212 response = app.post_json( '/run_completer_command', 213 event_data, 214 expect_errors = True ) 215 216 assert_that( response.status_code, 217 equal_to( requests.codes.internal_server_error ) ) 218 219 assert_that( response.json, 220 ErrorMatcher( RuntimeError, NO_DOCUMENTATION_MESSAGE ) ) 221 222 223@WithRetry 224@SharedYcmd 225def Subcommands_GetDoc_Method_test( app ): 226 filepath = PathToTestFile( 'simple_eclipse_project', 227 'src', 228 'com', 229 'test', 230 'AbstractTestWidget.java' ) 231 contents = ReadFile( filepath ) 232 233 event_data = BuildRequest( filepath = filepath, 234 filetype = 'java', 235 line_num = 17, 236 column_num = 17, 237 contents = contents, 238 command_arguments = [ 'GetDoc' ], 239 completer_target = 'filetype_default' ) 240 241 response = app.post_json( '/run_completer_command', event_data ).json 242 243 assert_that( response, has_entry( 'detailed_info', 244 'Return runtime debugging info. Useful for finding the ' 245 'actual code which is useful.' ) ) 246 247 248@WithRetry 249@SharedYcmd 250def Subcommands_GetDoc_Class_test( app ): 251 filepath = PathToTestFile( 'simple_eclipse_project', 252 'src', 253 'com', 254 'test', 255 'TestWidgetImpl.java' ) 256 contents = ReadFile( filepath ) 257 258 event_data = BuildRequest( filepath = filepath, 259 filetype = 'java', 260 line_num = 11, 261 column_num = 7, 262 contents = contents, 263 command_arguments = [ 'GetDoc' ], 264 completer_target = 'filetype_default' ) 265 266 response = app.post_json( '/run_completer_command', event_data ).json 267 268 assert_that( response, has_entry( 'detailed_info', 269 'This is the actual code that matters. This concrete ' 270 'implementation is the equivalent of the main function ' 271 'in other languages' ) ) 272 273 274@WithRetry 275@SharedYcmd 276def Subcommands_GetType_NoKnownType_test( app ): 277 filepath = PathToTestFile( 'simple_eclipse_project', 278 'src', 279 'com', 280 'test', 281 'TestWidgetImpl.java' ) 282 contents = ReadFile( filepath ) 283 284 event_data = BuildRequest( filepath = filepath, 285 filetype = 'java', 286 line_num = 28, 287 column_num = 1, 288 contents = contents, 289 command_arguments = [ 'GetType' ], 290 completer_target = 'filetype_default' ) 291 292 response = app.post_json( '/run_completer_command', 293 event_data, 294 expect_errors = True ) 295 296 assert_that( response.status_code, 297 equal_to( requests.codes.internal_server_error ) ) 298 299 assert_that( response.json, 300 ErrorMatcher( RuntimeError, 'Unknown type' ) ) 301 302 303@WithRetry 304@SharedYcmd 305def Subcommands_GetType_Class_test( app ): 306 filepath = PathToTestFile( 'simple_eclipse_project', 307 'src', 308 'com', 309 'test', 310 'TestWidgetImpl.java' ) 311 contents = ReadFile( filepath ) 312 313 event_data = BuildRequest( filepath = filepath, 314 filetype = 'java', 315 line_num = 11, 316 column_num = 7, 317 contents = contents, 318 command_arguments = [ 'GetType' ], 319 completer_target = 'filetype_default' ) 320 321 response = app.post_json( '/run_completer_command', event_data ).json 322 323 assert_that( response, has_entry( 'message', 'com.test.TestWidgetImpl' ) ) 324 325 326@WithRetry 327@SharedYcmd 328def Subcommands_GetType_Constructor_test( app ): 329 filepath = PathToTestFile( 'simple_eclipse_project', 330 'src', 331 'com', 332 'test', 333 'TestWidgetImpl.java' ) 334 contents = ReadFile( filepath ) 335 336 event_data = BuildRequest( filepath = filepath, 337 filetype = 'java', 338 line_num = 14, 339 column_num = 3, 340 contents = contents, 341 command_arguments = [ 'GetType' ], 342 completer_target = 'filetype_default' ) 343 344 response = app.post_json( '/run_completer_command', event_data ).json 345 346 assert_that( response, has_entry( 347 'message', 'com.test.TestWidgetImpl.TestWidgetImpl(String info)' ) ) 348 349 350@WithRetry 351@SharedYcmd 352def Subcommands_GetType_ClassMemberVariable_test( app ): 353 filepath = PathToTestFile( 'simple_eclipse_project', 354 'src', 355 'com', 356 'test', 357 'TestWidgetImpl.java' ) 358 contents = ReadFile( filepath ) 359 360 event_data = BuildRequest( filepath = filepath, 361 filetype = 'java', 362 line_num = 12, 363 column_num = 18, 364 contents = contents, 365 command_arguments = [ 'GetType' ], 366 completer_target = 'filetype_default' ) 367 368 response = app.post_json( '/run_completer_command', event_data ).json 369 370 assert_that( response, has_entry( 'message', 'String info' ) ) 371 372 373@WithRetry 374@SharedYcmd 375def Subcommands_GetType_MethodArgument_test( app ): 376 filepath = PathToTestFile( 'simple_eclipse_project', 377 'src', 378 'com', 379 'test', 380 'TestWidgetImpl.java' ) 381 contents = ReadFile( filepath ) 382 383 event_data = BuildRequest( filepath = filepath, 384 filetype = 'java', 385 line_num = 16, 386 column_num = 17, 387 contents = contents, 388 command_arguments = [ 'GetType' ], 389 completer_target = 'filetype_default' ) 390 391 response = app.post_json( '/run_completer_command', event_data ).json 392 393 assert_that( response, has_entry( 394 'message', 'String info - com.test.TestWidgetImpl.TestWidgetImpl(String)' 395 ) ) 396 397 398@WithRetry 399@SharedYcmd 400def Subcommands_GetType_MethodVariable_test( app ): 401 filepath = PathToTestFile( 'simple_eclipse_project', 402 'src', 403 'com', 404 'test', 405 'TestWidgetImpl.java' ) 406 contents = ReadFile( filepath ) 407 408 event_data = BuildRequest( filepath = filepath, 409 filetype = 'java', 410 line_num = 15, 411 column_num = 9, 412 contents = contents, 413 command_arguments = [ 'GetType' ], 414 completer_target = 'filetype_default' ) 415 416 response = app.post_json( '/run_completer_command', event_data ).json 417 418 assert_that( response, has_entry( 419 'message', 'int a - com.test.TestWidgetImpl.TestWidgetImpl(String)' ) ) 420 421 422@WithRetry 423@SharedYcmd 424def Subcommands_GetType_Method_test( app ): 425 filepath = PathToTestFile( 'simple_eclipse_project', 426 'src', 427 'com', 428 'test', 429 'TestWidgetImpl.java' ) 430 contents = ReadFile( filepath ) 431 432 event_data = BuildRequest( filepath = filepath, 433 filetype = 'java', 434 line_num = 20, 435 column_num = 15, 436 contents = contents, 437 command_arguments = [ 'GetType' ], 438 completer_target = 'filetype_default' ) 439 440 response = app.post_json( '/run_completer_command', event_data ).json 441 442 assert_that( response, has_entry( 443 'message', 'void com.test.TestWidgetImpl.doSomethingVaguelyUseful()' ) ) 444 445 446@WithRetry 447@SharedYcmd 448def Subcommands_GetType_Unicode_test( app ): 449 contents = ReadFile( TEST_JAVA ) 450 451 app.post_json( '/event_notification', 452 BuildRequest( filepath = TEST_JAVA, 453 filetype = 'java', 454 contents = contents, 455 event_name = 'FileReadyToParse' ) ) 456 457 event_data = BuildRequest( filepath = TEST_JAVA, 458 filetype = 'java', 459 line_num = 7, 460 column_num = 17, 461 contents = contents, 462 command_arguments = [ 'GetType' ], 463 completer_target = 'filetype_default' ) 464 465 response = app.post_json( '/run_completer_command', event_data ).json 466 467 assert_that( response, has_entry( 468 'message', 'String whåtawîdgé - com.youcompleteme.Test.doUnicødeTes()' ) ) 469 470 471@WithRetry 472@SharedYcmd 473def Subcommands_GetType_LiteralValue_test( app ): 474 filepath = PathToTestFile( 'simple_eclipse_project', 475 'src', 476 'com', 477 'test', 478 'TestWidgetImpl.java' ) 479 contents = ReadFile( filepath ) 480 481 event_data = BuildRequest( filepath = filepath, 482 filetype = 'java', 483 line_num = 15, 484 column_num = 13, 485 contents = contents, 486 command_arguments = [ 'GetType' ], 487 completer_target = 'filetype_default' ) 488 489 response = app.post_json( '/run_completer_command', 490 event_data, 491 expect_errors = True ) 492 493 assert_that( response.status_code, 494 equal_to( requests.codes.internal_server_error ) ) 495 496 assert_that( response.json, 497 ErrorMatcher( RuntimeError, 'Unknown type' ) ) 498 499 500@WithRetry 501@SharedYcmd 502def Subcommands_GoTo_NoLocation_test( app ): 503 filepath = PathToTestFile( 'simple_eclipse_project', 504 'src', 505 'com', 506 'test', 507 'AbstractTestWidget.java' ) 508 contents = ReadFile( filepath ) 509 510 event_data = BuildRequest( filepath = filepath, 511 filetype = 'java', 512 line_num = 18, 513 column_num = 1, 514 contents = contents, 515 command_arguments = [ 'GoTo' ], 516 completer_target = 'filetype_default' ) 517 518 response = app.post_json( '/run_completer_command', 519 event_data, 520 expect_errors = True ) 521 522 assert_that( response.status_code, 523 equal_to( requests.codes.internal_server_error ) ) 524 525 assert_that( response.json, 526 ErrorMatcher( RuntimeError, 'Cannot jump to location' ) ) 527 528 529@WithRetry 530@SharedYcmd 531def Subcommands_GoToReferences_NoReferences_test( app ): 532 filepath = PathToTestFile( 'simple_eclipse_project', 533 'src', 534 'com', 535 'test', 536 'AbstractTestWidget.java' ) 537 contents = ReadFile( filepath ) 538 539 event_data = BuildRequest( filepath = filepath, 540 filetype = 'java', 541 line_num = 2, 542 column_num = 1, 543 contents = contents, 544 command_arguments = [ 'GoToReferences' ], 545 completer_target = 'filetype_default' ) 546 547 response = app.post_json( '/run_completer_command', 548 event_data, 549 expect_errors = True ) 550 551 assert_that( response.status_code, 552 equal_to( requests.codes.internal_server_error ) ) 553 554 assert_that( response.json, 555 ErrorMatcher( RuntimeError, 556 'Cannot jump to location' ) ) 557 558 559@WithRetry 560@IsolatedYcmd( { 561 'extra_conf_globlist': PathToTestFile( 'multiple_projects', '*' ) 562} ) 563def Subcommands_GoToReferences_MultipleProjects_test( app ): 564 filepath = PathToTestFile( 'multiple_projects', 565 'src', 566 'core', 567 'java', 568 'com', 569 'puremourning', 570 'widget', 571 'core', 572 'Utils.java' ) 573 StartJavaCompleterServerWithFile( app, filepath ) 574 575 576 RunTest( app, { 577 'description': 'GoToReferences works across multiple projects', 578 'request': { 579 'command': 'GoToReferences', 580 'filepath': filepath, 581 'line_num': 5, 582 'column_num': 22, 583 }, 584 'expect': { 585 'response': requests.codes.ok, 586 'data': contains_inanyorder( 587 LocationMatcher( filepath, 8, 35 ), 588 LocationMatcher( PathToTestFile( 'multiple_projects', 589 'src', 590 'input', 591 'java', 592 'com', 593 'puremourning', 594 'widget', 595 'input', 596 'InputApp.java' ), 597 8, 598 16 ) 599 ) 600 } 601 } ) 602 603 604 605@WithRetry 606@SharedYcmd 607def Subcommands_GoToReferences_test( app ): 608 filepath = PathToTestFile( 'simple_eclipse_project', 609 'src', 610 'com', 611 'test', 612 'AbstractTestWidget.java' ) 613 contents = ReadFile( filepath ) 614 615 event_data = BuildRequest( filepath = filepath, 616 filetype = 'java', 617 line_num = 10, 618 column_num = 15, 619 contents = contents, 620 command_arguments = [ 'GoToReferences' ], 621 completer_target = 'filetype_default' ) 622 623 response = app.post_json( '/run_completer_command', event_data ).json 624 625 assert_that( response, contains_exactly( has_entries( { 626 'filepath': PathToTestFile( 'simple_eclipse_project', 627 'src', 628 'com', 629 'test', 630 'TestFactory.java' ), 631 'column_num': 9, 632 'description': " w.doSomethingVaguelyUseful();", 633 'line_num': 28 634 } ), 635 has_entries( { 636 'filepath': PathToTestFile( 'simple_eclipse_project', 637 'src', 638 'com', 639 'test', 640 'TestLauncher.java' ), 641 'column_num': 11, 642 'description': " w.doSomethingVaguelyUseful();", 643 'line_num': 32 644 } ) ) ) 645 646 647@WithRetry 648@SharedYcmd 649def Subcommands_GoToSymbol_SingleSameFile_test( app ): 650 contents = ReadFile( TEST_JAVA ) 651 652 event_data = BuildRequest( filepath = TEST_JAVA, 653 filetype = 'java', 654 line_num = 1, 655 column_num = 1, 656 contents = contents, 657 command_arguments = [ 'GoToSymbol', 'TéstClass' ], 658 completer_target = 'filetype_default' ) 659 660 response = app.post_json( '/run_completer_command', event_data ).json 661 662 assert_that( response, has_entries( { 663 'filepath': TEST_JAVA, 664 'description': "Class: TéstClass", 665 'line_num': 20, 666 'column_num': 16, 667 } ) ) 668 669 670@WithRetry 671@SharedYcmd 672def Subcommands_GoToSymbol_Multiple_test( app ): 673 contents = ReadFile( TEST_JAVA ) 674 675 event_data = BuildRequest( filepath = TEST_JAVA, 676 filetype = 'java', 677 line_num = 1, 678 column_num = 1, 679 contents = contents, 680 command_arguments = [ 'GoToSymbol', 'test' ], 681 completer_target = 'filetype_default' ) 682 683 response = app.post_json( '/run_completer_command', event_data ).json 684 685 assert_that( response, contains_inanyorder( 686 has_entries( { 687 'filepath': PathToTestFile( 'simple_eclipse_project', 688 'src', 689 'com', 690 'test', 691 'TestFactory.java' ) , 692 'description': "Class: TestFactory", 693 'line_num': 12, 694 'column_num': 14, 695 } ), 696 has_entries( { 697 'filepath': PathToTestFile( 'simple_eclipse_project', 698 'src', 699 'com', 700 'test', 701 'TestWidgetImpl.java' ) , 702 'description': "Class: TestWidgetImpl", 703 'line_num': 11, 704 'column_num': 7, 705 } ), 706 has_entries( { 707 'filepath': PathToTestFile( 'simple_eclipse_project', 708 'src', 709 'com', 710 'test', 711 'TestLauncher.java' ) , 712 'description': "Class: TestLauncher", 713 'line_num': 6, 714 'column_num': 7, 715 } ), 716 has_entries( { 717 'filepath': PathToTestFile( 'simple_eclipse_project', 718 'src', 719 'com', 720 'youcompleteme', 721 'Test.java' ) , 722 'description': "Class: Test", 723 'line_num': 3, 724 'column_num': 14, 725 } ), 726 has_entries( { 727 'filepath': PathToTestFile( 'simple_eclipse_project', 728 'src', 729 'com', 730 'test', 731 'TestWithDocumentation.java' ) , 732 'description': "Class: TestWithDocumentation", 733 'line_num': 3, 734 'column_num': 14, 735 } ) 736 ) ) 737 738 739@WithRetry 740@SharedYcmd 741def Subcommands_GoToSymbol_None_test( app ): 742 contents = ReadFile( TEST_JAVA ) 743 744 event_data = BuildRequest( filepath = TEST_JAVA, 745 filetype = 'java', 746 line_num = 1, 747 column_num = 1, 748 contents = contents, 749 command_arguments = [ 'GoToSymbol', 'abcd' ], 750 completer_target = 'filetype_default' ) 751 752 response = app.post_json( '/run_completer_command', 753 event_data, 754 expect_errors = True ) 755 756 assert_that( response.status_code, 757 equal_to( requests.codes.internal_server_error ) ) 758 759 assert_that( response.json, 760 ErrorMatcher( RuntimeError, 'Symbol not found' ) ) 761 762 763 764@WithRetry 765@SharedYcmd 766def Subcommands_RefactorRename_Simple_test( app ): 767 filepath = PathToTestFile( 'simple_eclipse_project', 768 'src', 769 'com', 770 'test', 771 'TestLauncher.java' ) 772 RunTest( app, { 773 'description': 'RefactorRename works within a single scope/file', 774 'request': { 775 'command': 'RefactorRename', 776 'arguments': [ 'renamed_l' ], 777 'filepath': filepath, 778 'line_num': 28, 779 'column_num': 5, 780 }, 781 'expect': { 782 'response': requests.codes.ok, 783 'data': has_entries( { 784 'fixits': contains_exactly( has_entries( { 785 'chunks': contains_exactly( 786 ChunkMatcher( 'renamed_l = new TestLauncher( 10 );' 787 '\n renamed_l', 788 LocationMatcher( filepath, 27, 18 ), 789 LocationMatcher( filepath, 28, 6 ) ), 790 ), 791 'location': LocationMatcher( filepath, 28, 5 ) 792 } ) ) 793 } ) 794 } 795 } ) 796 797 798@ExpectedFailure( 'Renaming does not work on overridden methods ' 799 'since jdt.ls 0.21.0', 800 matches_regexp( 'No item matched:.*TestWidgetImpl.java' ) ) 801@WithRetry 802@SharedYcmd 803def Subcommands_RefactorRename_MultipleFiles_test( app ): 804 AbstractTestWidget = PathToTestFile( 'simple_eclipse_project', 805 'src', 806 'com', 807 'test', 808 'AbstractTestWidget.java' ) 809 TestFactory = PathToTestFile( 'simple_eclipse_project', 810 'src', 811 'com', 812 'test', 813 'TestFactory.java' ) 814 TestLauncher = PathToTestFile( 'simple_eclipse_project', 815 'src', 816 'com', 817 'test', 818 'TestLauncher.java' ) 819 TestWidgetImpl = PathToTestFile( 'simple_eclipse_project', 820 'src', 821 'com', 822 'test', 823 'TestWidgetImpl.java' ) 824 825 RunTest( app, { 826 'description': 'RefactorRename works across files', 827 'request': { 828 'command': 'RefactorRename', 829 'arguments': [ 'a_quite_long_string' ], 830 'filepath': TestLauncher, 831 'line_num': 32, 832 'column_num': 13, 833 }, 834 'expect': { 835 'response': requests.codes.ok, 836 'data': has_entries( { 837 'fixits': contains_exactly( has_entries( { 838 'chunks': contains_exactly( 839 ChunkMatcher( 840 'a_quite_long_string', 841 LocationMatcher( AbstractTestWidget, 10, 15 ), 842 LocationMatcher( AbstractTestWidget, 10, 39 ) ), 843 ChunkMatcher( 844 'a_quite_long_string', 845 LocationMatcher( TestFactory, 28, 9 ), 846 LocationMatcher( TestFactory, 28, 33 ) ), 847 ChunkMatcher( 848 'a_quite_long_string', 849 LocationMatcher( TestLauncher, 32, 11 ), 850 LocationMatcher( TestLauncher, 32, 35 ) ), 851 ChunkMatcher( 852 'a_quite_long_string', 853 LocationMatcher( TestWidgetImpl, 20, 15 ), 854 LocationMatcher( TestWidgetImpl, 20, 39 ) ), 855 ), 856 'location': LocationMatcher( TestLauncher, 32, 13 ) 857 } ) ) 858 } ) 859 } 860 } ) 861 862 863@WithRetry 864@SharedYcmd 865def Subcommands_RefactorRename_Missing_New_Name_test( app ): 866 filepath = PathToTestFile( 'simple_eclipse_project', 867 'src', 868 'com', 869 'test', 870 'TestLauncher.java' ) 871 RunTest( app, { 872 'description': 'RefactorRename raises an error without new name', 873 'request': { 874 'command': 'RefactorRename', 875 'line_num': 15, 876 'column_num': 5, 877 'filepath': filepath, 878 }, 879 'expect': { 880 'response': requests.codes.internal_server_error, 881 'data': ErrorMatcher( ValueError, 882 'Please specify a new name to rename it to.\n' 883 'Usage: RefactorRename <new name>' ), 884 } 885 } ) 886 887 888@WithRetry 889@SharedYcmd 890def Subcommands_RefactorRename_Unicode_test( app ): 891 RunTest( app, { 892 'description': 'Rename works for unicode identifier', 893 'request': { 894 'command': 'RefactorRename', 895 'arguments': [ 'shorter' ], 896 'line_num': 7, 897 'column_num': 21, 898 'filepath': TEST_JAVA, 899 }, 900 'expect': { 901 'response': requests.codes.ok, 902 'data': has_entries( { 903 'fixits': contains_exactly( has_entries( { 904 'chunks': contains_exactly( 905 ChunkMatcher( 906 'shorter = "Test";\n return shorter', 907 LocationMatcher( TEST_JAVA, 7, 12 ), 908 LocationMatcher( TEST_JAVA, 8, 25 ) 909 ), 910 ), 911 } ) ), 912 } ), 913 }, 914 } ) 915 916 917 918def RunFixItTest( app, description, filepath, line, col, fixits_for_line ): 919 RunTest( app, { 920 'description': description, 921 'request': { 922 'command': 'FixIt', 923 'line_num': line, 924 'column_num': col, 925 'filepath': filepath, 926 }, 927 'expect': { 928 'response': requests.codes.ok, 929 'data': fixits_for_line, 930 } 931 } ) 932 933 934@WithRetry 935@pytest.mark.parametrize( 'description,column', [ 936 ( 'FixIt works at the firtst char of the line', 1 ), 937 ( 'FixIt works at the begin of the range of the diag.', 15 ), 938 ( 'FixIt works at the end of the range of the diag.', 20 ), 939 ( 'FixIt works at the end of the line', 34 ), 940] ) 941@SharedYcmd 942def Subcommands_FixIt_SingleDiag_MultipleOption_Insertion_test( app, 943 description, 944 column ): 945 import os 946 wibble_path = PathToTestFile( 'simple_eclipse_project', 947 'src', 948 'com', 949 'test', 950 'Wibble.java' ) 951 wibble_text = 'package com.test;{0}{0}public {1} Wibble {{{0}{0}}}{0}' 952 filepath = PathToTestFile( 'simple_eclipse_project', 953 'src', 954 'com', 955 'test', 956 'TestFactory.java' ) 957 958 # Note: The code actions for creating variables are really not very useful. 959 # The import is, however, and the FixIt almost exactly matches the one 960 # supplied when completing 'CUTHBERT' and auto-inserting. 961 fixits_for_line = has_entries( { 962 'fixits': contains_inanyorder( 963 has_entries( { 964 'text': "Import 'Wibble' (com.test.wobble)", 965 'kind': 'quickfix', 966 'chunks': contains_exactly( 967 ChunkMatcher( 'package com.test;\n\n' 968 'import com.test.wobble.Wibble;\n\n', 969 LocationMatcher( filepath, 1, 1 ), 970 LocationMatcher( filepath, 3, 1 ) ), 971 ), 972 } ), 973 has_entries( { 974 'text': "Create constant 'Wibble'", 975 'kind': 'quickfix', 976 'chunks': contains_exactly( 977 ChunkMatcher( '\n\nprivate static final String Wibble = null;', 978 LocationMatcher( filepath, 16, 4 ), 979 LocationMatcher( filepath, 16, 4 ) ), 980 ), 981 } ), 982 has_entries( { 983 'text': "Create class 'Wibble'", 984 'kind': 'quickfix', 985 'chunks': contains_exactly( 986 ChunkMatcher( wibble_text.format( os.linesep, 'class' ), 987 LocationMatcher( wibble_path, 1, 1 ), 988 LocationMatcher( wibble_path, 1, 1 ) ), 989 ), 990 } ), 991 has_entries( { 992 'text': "Create interface 'Wibble'", 993 'kind': 'quickfix', 994 'chunks': contains_exactly( 995 ChunkMatcher( wibble_text.format( os.linesep, 'interface' ), 996 LocationMatcher( wibble_path, 1, 1 ), 997 LocationMatcher( wibble_path, 1, 1 ) ), 998 ), 999 } ), 1000 has_entries( { 1001 'text': "Create enum 'Wibble'", 1002 'kind': 'quickfix', 1003 'chunks': contains_exactly( 1004 ChunkMatcher( wibble_text.format( os.linesep, 'enum' ), 1005 LocationMatcher( wibble_path, 1, 1 ), 1006 LocationMatcher( wibble_path, 1, 1 ) ), 1007 ), 1008 } ), 1009 has_entries( { 1010 'text': "Create local variable 'Wibble'", 1011 'kind': 'quickfix', 1012 'chunks': contains_exactly( 1013 ChunkMatcher( 'Object Wibble;\n\t', 1014 LocationMatcher( filepath, 19, 5 ), 1015 LocationMatcher( filepath, 19, 5 ) ), 1016 ), 1017 } ), 1018 has_entries( { 1019 'text': "Create field 'Wibble'", 1020 'kind': 'quickfix', 1021 'chunks': contains_exactly( 1022 ChunkMatcher( '\n\nprivate Object Wibble;', 1023 LocationMatcher( filepath, 16, 4 ), 1024 LocationMatcher( filepath, 16, 4 ) ), 1025 ), 1026 } ), 1027 has_entries( { 1028 'text': "Create parameter 'Wibble'", 1029 'kind': 'quickfix', 1030 'chunks': contains_exactly( 1031 ChunkMatcher( ', Object Wibble', 1032 LocationMatcher( filepath, 18, 32 ), 1033 LocationMatcher( filepath, 18, 32 ) ), 1034 ), 1035 } ), 1036 has_entries( { 1037 'text': 'Generate toString()...', 1038 'kind': 'source.generate.toString', 1039 'chunks': contains_exactly( 1040 ChunkMatcher( '\n\n@Override\npublic String toString() {' 1041 '\n\treturn "TestFactory []";\n}', 1042 LocationMatcher( filepath, 32, 4 ), 1043 LocationMatcher( filepath, 32, 4 ) ), 1044 ), 1045 } ), 1046 has_entries( { 1047 'text': 'Organize imports', 1048 'kind': 'source.organizeImports', 1049 'chunks': contains_exactly( 1050 ChunkMatcher( '\n\nimport com.test.wobble.Wibble;\n\n', 1051 LocationMatcher( filepath, 1, 18 ), 1052 LocationMatcher( filepath, 3, 1 ) ), 1053 ), 1054 } ), 1055 has_entries( { 1056 'text': 'Change modifiers to final where possible', 1057 'kind': 'source.generate.finalModifiers', 1058 'chunks': contains_exactly( 1059 ChunkMatcher( 'final Wibble w ) {\n if ( w == Wibble.CUTHBERT ) {' 1060 '\n }\n }\n\n public AbstractTestWidget getWidget' 1061 '( final String info ) {\n final AbstractTestWidget' 1062 ' w = new TestWidgetImpl( info );\n final ', 1063 LocationMatcher( filepath, 18, 24 ), 1064 LocationMatcher( filepath, 25, 5 ) ), 1065 ), 1066 } ), 1067 ) 1068 } ) 1069 1070 RunFixItTest( app, description, filepath, 19, column, fixits_for_line ) 1071 1072 1073@WithRetry 1074@SharedYcmd 1075def Subcommands_FixIt_SingleDiag_SingleOption_Modify_test( app ): 1076 filepath = PathToTestFile( 'simple_eclipse_project', 1077 'src', 1078 'com', 1079 'test', 1080 'TestFactory.java' ) 1081 1082 # TODO: As there is only one option, we automatically apply it. 1083 # In Java case this might not be the right thing. It's a code assist, not a 1084 # FixIt really. Perhaps we should change the client to always ask for 1085 # confirmation? 1086 fixits = has_entries( { 1087 'fixits': contains_inanyorder( 1088 has_entries( { 1089 'text': "Change type of 'test' to 'boolean'", 1090 'kind': 'quickfix', 1091 'chunks': contains_exactly( 1092 ChunkMatcher( 'boolean', 1093 LocationMatcher( filepath, 14, 12 ), 1094 LocationMatcher( filepath, 14, 15 ) ), 1095 ), 1096 } ), 1097 has_entries( { 1098 'text': 'Generate toString()...', 1099 'kind': 'source.generate.toString', 1100 'chunks': contains_exactly( 1101 ChunkMatcher( '\n\n@Override\npublic String toString() {' 1102 '\n\treturn "TestFactory []";\n}', 1103 LocationMatcher( filepath, 32, 4 ), 1104 LocationMatcher( filepath, 32, 4 ) ), 1105 ), 1106 } ), 1107 has_entries( { 1108 'text': 'Organize imports', 1109 'kind': 'source.organizeImports', 1110 'chunks': contains_exactly( 1111 ChunkMatcher( '\n\nimport com.test.wobble.Wibble;\n\n', 1112 LocationMatcher( filepath, 1, 18 ), 1113 LocationMatcher( filepath, 3, 1 ) ), 1114 ), 1115 } ), 1116 has_entries( { 1117 'text': 'Change modifiers to final where possible', 1118 'kind': 'source.generate.finalModifiers', 1119 'chunks': contains_exactly( 1120 ChunkMatcher( 'final Wibble w ) {\n if ( w == Wibble.CUTHBERT ) {' 1121 '\n }\n }\n\n public AbstractTestWidget getWidget' 1122 '( final String info ) {\n final AbstractTestWidget' 1123 ' w = new TestWidgetImpl( info );\n final ', 1124 LocationMatcher( filepath, 18, 24 ), 1125 LocationMatcher( filepath, 25, 5 ) ), 1126 ), 1127 } ), 1128 ) 1129 } ) 1130 1131 RunFixItTest( app, 'FixIts can change lines as well as add them', 1132 filepath, 27, 12, fixits ) 1133 1134 1135@WithRetry 1136@SharedYcmd 1137def Subcommands_FixIt_SingleDiag_MultiOption_Delete_test( app ): 1138 filepath = PathToTestFile( 'simple_eclipse_project', 1139 'src', 1140 'com', 1141 'test', 1142 'TestFactory.java' ) 1143 1144 fixits = has_entries( { 1145 'fixits': contains_inanyorder( 1146 has_entries( { 1147 'text': "Remove 'testString', keep assignments with side effects", 1148 'kind': 'quickfix', 1149 'chunks': contains_exactly( 1150 ChunkMatcher( '', 1151 LocationMatcher( filepath, 14, 21 ), 1152 LocationMatcher( filepath, 15, 30 ) ), 1153 ), 1154 } ), 1155 # The edit reported for this is huge and uninteresting really. Manual 1156 # testing can show that it works. This test is really about the previous 1157 # FixIt (and nonetheless, the previous tests ensure that we correctly 1158 # populate the chunks list; the contents all come from jdt.ls) 1159 has_entries( { 1160 'text': "Create getter and setter for 'testString'", 1161 'chunks': instance_of( list ) 1162 } ), 1163 has_entries( { 1164 'text': "Organize imports", 1165 'chunks': instance_of( list ) 1166 } ), 1167 has_entries( { 1168 'text': "Generate Getters and Setters", 1169 'chunks': instance_of( list ) 1170 } ), 1171 has_entries( { 1172 'text': 'Change modifiers to final where possible', 1173 'chunks': instance_of( list ) 1174 } ), 1175 ) 1176 } ) 1177 1178 RunFixItTest( app, 'FixIts can change lines as well as add them', 1179 filepath, 15, 29, fixits ) 1180 1181 1182@WithRetry 1183@pytest.mark.parametrize( 'description,column,expect_fixits', [ 1184 ( 'diags are merged in FixIt options - start of line', 1, 'MERGE' ), 1185 ( 'diags are not merged in FixIt options - start of diag 1', 10, 'FIRST' ), 1186 ( 'diags are not merged in FixIt options - end of diag 1', 15, 'FIRST' ), 1187 ( 'diags are not merged in FixIt options - start of diag 2', 23, 'SECOND' ), 1188 ( 'diags are not merged in FixIt options - end of diag 2', 46, 'SECOND' ), 1189 ( 'diags are merged in FixIt options - end of line', 55, 'MERGE' ), 1190] ) 1191@SharedYcmd 1192def Subcommands_FixIt_MultipleDiags_test( app, 1193 description, 1194 column, 1195 expect_fixits ): 1196 filepath = PathToTestFile( 'simple_eclipse_project', 1197 'src', 1198 'com', 1199 'test', 1200 'TestFactory.java' ) 1201 1202 FIRST = [ 1203 has_entries( { 1204 'text': "Change type of 'test' to 'boolean'", 1205 'kind': 'quickfix', 1206 'chunks': contains_exactly( 1207 ChunkMatcher( 'boolean', 1208 LocationMatcher( filepath, 14, 12 ), 1209 LocationMatcher( filepath, 14, 15 ) ), 1210 ), 1211 } ), 1212 ] 1213 SECOND = [ 1214 has_entries( { 1215 'text': "Remove argument to match 'doSomethingVaguelyUseful()'", 1216 'kind': 'quickfix', 1217 'chunks': contains_exactly( 1218 ChunkMatcher( '', 1219 LocationMatcher( filepath, 30, 48 ), 1220 LocationMatcher( filepath, 30, 50 ) ), 1221 ), 1222 } ), 1223 has_entries( { 1224 'text': "Change method 'doSomethingVaguelyUseful()': Add parameter " 1225 "'Bar'", 1226 'chunks': instance_of( list ), 1227 } ), 1228 has_entries( { 1229 'text': "Create method 'doSomethingVaguelyUseful(Bar)' in type " 1230 "'AbstractTestWidget'", 1231 'chunks': instance_of( list ), 1232 } ), 1233 ] 1234 1235 ACTIONS = [ 1236 has_entries( { 1237 'text': "Generate toString()...", 1238 'chunks': instance_of( list ), 1239 } ), 1240 has_entries( { 1241 'text': "Organize imports", 1242 'chunks': instance_of( list ), 1243 } ), 1244 has_entries( { 1245 'text': 'Change modifiers to final where possible', 1246 'chunks': instance_of( list ), 1247 } ), 1248 ] 1249 1250 FIXITS = { 1251 'FIRST': FIRST + ACTIONS, 1252 'SECOND': SECOND + ACTIONS, 1253 'MERGE': FIRST + SECOND + ACTIONS, 1254 } 1255 1256 fixits = has_entries( { 1257 'fixits': contains_inanyorder( *FIXITS[ expect_fixits ] ) 1258 } ) 1259 1260 RunFixItTest( app, description, filepath, 30, column, fixits ) 1261 1262 1263@SharedYcmd 1264def Subcommands_FixIt_Range_test( app ): 1265 filepath = PathToTestFile( 'simple_eclipse_project', 1266 'src', 1267 'com', 1268 'test', 1269 'TestLauncher.java' ) 1270 RunTest( app, { 1271 'description': 'Formatting is applied on some part of the file ' 1272 'with tabs composed of 4 spaces', 1273 'request': { 1274 'command': 'FixIt', 1275 'filepath': filepath, 1276 'range': { 1277 'start': { 1278 'line_num': 34, 1279 'column_num': 28, 1280 }, 1281 'end': { 1282 'line_num': 34, 1283 'column_num': 73 1284 } 1285 }, 1286 }, 1287 'expect': { 1288 'response': requests.codes.ok, 1289 'data': has_entries( { 1290 'fixits': contains_inanyorder( 1291 has_entries( { 1292 'text': 'Extract to field', 1293 'kind': 'refactor.extract.field', 1294 'chunks': contains_exactly( 1295 ChunkMatcher( 1296 matches_regexp( 1297 'private String \\w+;\n' 1298 '\n' 1299 '\t@Override\n' 1300 ' public void launch\\(\\) {\n' 1301 ' AbstractTestWidget w = ' 1302 'factory.getWidget\\( "Test" \\);\n' 1303 ' ' 1304 'w.doSomethingVaguelyUseful\\(\\);\n' 1305 '\n' 1306 ' \\w+ = "Did something ' 1307 'useful: " \\+ w.getWidgetInfo\\(\\);\n' 1308 '\t\tSystem.out.println\\( \\w+' ), 1309 LocationMatcher( filepath, 29, 7 ), 1310 LocationMatcher( filepath, 34, 73 ) ), 1311 ), 1312 } ), 1313 has_entries( { 1314 'text': 'Extract to method', 1315 'kind': 'refactor.extract.function', 1316 'chunks': contains_exactly( 1317 # This one is a wall of text that rewrites 35 lines 1318 ChunkMatcher( instance_of( str ), 1319 LocationMatcher( filepath, 1, 1 ), 1320 LocationMatcher( filepath, 35, 8 ) ), 1321 ), 1322 } ), 1323 has_entries( { 1324 'text': 'Extract to local variable (replace all occurrences)', 1325 'kind': 'refactor.extract.variable', 1326 'chunks': contains_exactly( 1327 ChunkMatcher( 1328 matches_regexp( 1329 'String \\w+ = "Did something ' 1330 'useful: " \\+ w.getWidgetInfo\\(\\);\n' 1331 '\t\tSystem.out.println\\( \\w+' ), 1332 LocationMatcher( filepath, 34, 9 ), 1333 LocationMatcher( filepath, 34, 73 ) ), 1334 ), 1335 } ), 1336 has_entries( { 1337 'text': 'Extract to local variable', 1338 'kind': 'refactor.extract.variable', 1339 'chunks': contains_exactly( 1340 ChunkMatcher( 1341 matches_regexp( 1342 'String \\w+ = "Did something ' 1343 'useful: " \\+ w.getWidgetInfo\\(\\);\n' 1344 '\t\tSystem.out.println\\( \\w+' ), 1345 LocationMatcher( filepath, 34, 9 ), 1346 LocationMatcher( filepath, 34, 73 ) ), 1347 ), 1348 } ), 1349 has_entries( { 1350 'text': 'Introduce Parameter...', 1351 'kind': 'refactor.introduce.parameter', 1352 'chunks': contains_exactly( 1353 ChunkMatcher( 1354 'String string) {\n' 1355 ' AbstractTestWidget w = factory.getWidget( "Test" );\n' 1356 ' w.doSomethingVaguelyUseful();\n' 1357 '\n' 1358 ' System.out.println( string', 1359 LocationMatcher( filepath, 30, 26 ), 1360 LocationMatcher( filepath, 34, 73 ) ), 1361 ), 1362 } ), 1363 has_entries( { 1364 'text': 'Organize imports', 1365 'chunks': instance_of( list ), 1366 } ), 1367 has_entries( { 1368 'text': 'Change modifiers to final where possible', 1369 'chunks': instance_of( list ), 1370 } ), 1371 ) 1372 } ) 1373 } 1374 } ) 1375 1376 1377 1378@WithRetry 1379@SharedYcmd 1380def Subcommands_FixIt_NoDiagnostics_test( app ): 1381 filepath = PathToTestFile( 'simple_eclipse_project', 1382 'src', 1383 'com', 1384 'test', 1385 'TestFactory.java' ) 1386 1387 RunFixItTest( app, "no FixIts means you gotta code it yo' self", 1388 filepath, 1, 1, has_entries( { 1389 'fixits': contains_inanyorder( 1390 has_entries( { 1391 'text': 'Change modifiers to final where possible', 1392 'chunks': instance_of( list ) } ), 1393 has_entries( { 'text': 'Organize imports', 1394 'chunks': instance_of( list ) } ), 1395 has_entries( { 'text': 'Generate toString()...', 1396 'chunks': instance_of( list ) } ) ) } ) ) 1397 1398 1399@WithRetry 1400@SharedYcmd 1401def Subcommands_FixIt_Unicode_test( app ): 1402 fixits = has_entries( { 1403 'fixits': contains_inanyorder( 1404 has_entries( { 1405 'text': "Remove argument to match 'doUnicødeTes()'", 1406 'kind': 'quickfix', 1407 'chunks': contains_exactly( 1408 ChunkMatcher( '', 1409 LocationMatcher( TEST_JAVA, 13, 24 ), 1410 LocationMatcher( TEST_JAVA, 13, 29 ) ), 1411 ), 1412 } ), 1413 has_entries( { 1414 'text': "Change method 'doUnicødeTes()': Add parameter 'String'", 1415 'kind': 'quickfix', 1416 'chunks': contains_exactly( 1417 ChunkMatcher( 'String test2', 1418 LocationMatcher( TEST_JAVA, 6, 31 ), 1419 LocationMatcher( TEST_JAVA, 6, 31 ) ), 1420 ), 1421 } ), 1422 has_entries( { 1423 'text': "Create method 'doUnicødeTes(String)'", 1424 'kind': 'quickfix', 1425 'chunks': contains_exactly( 1426 ChunkMatcher( 'private void doUnicødeTes(String test2) {\n}\n\n\n', 1427 LocationMatcher( TEST_JAVA, 20, 3 ), 1428 LocationMatcher( TEST_JAVA, 20, 3 ) ), 1429 ), 1430 } ), 1431 has_entries( { 1432 'text': 'Change modifiers to final where possible', 1433 'chunks': instance_of( list ), 1434 } ), 1435 has_entries( { 1436 'text': "Generate Getters and Setters", 1437 'chunks': instance_of( list ), 1438 } ), 1439 ) 1440 } ) 1441 1442 RunFixItTest( app, 'FixIts and diagnostics work with unicode strings', 1443 TEST_JAVA, 13, 1, fixits ) 1444 1445 1446@WithRetry 1447@IsolatedYcmd() 1448def Subcommands_FixIt_InvalidURI_test( app ): 1449 filepath = PathToTestFile( 'simple_eclipse_project', 1450 'src', 1451 'com', 1452 'test', 1453 'TestFactory.java' ) 1454 1455 fixits = has_entries( { 1456 'fixits': contains_inanyorder( 1457 has_entries( { 1458 'kind': 'quickfix', 1459 'text': "Change type of 'test' to 'boolean'", 1460 'chunks': contains_exactly( 1461 ChunkMatcher( 'boolean', 1462 LocationMatcher( '', 14, 12 ), 1463 LocationMatcher( '', 14, 15 ) ), 1464 ), 1465 } ), 1466 has_entries( { 1467 'text': 'Organize imports', 1468 'kind': 'source.organizeImports', 1469 'chunks': contains_exactly( 1470 ChunkMatcher( '\n\nimport com.test.wobble.Wibble;\n\n', 1471 LocationMatcher( '', 1, 1 ), 1472 LocationMatcher( '', 3, 1 ) ), 1473 ), 1474 } ), 1475 has_entries( { 1476 'text': 'Change modifiers to final where possible', 1477 'kind': 'source.generate.finalModifiers', 1478 'chunks': contains_exactly( 1479 ChunkMatcher( "final Wibble w ) {\n if ( w == Wibble.CUTHBERT ) {" 1480 "\n }\n }\n\n public AbstractTestWidget getWidget" 1481 "( final String info ) {\n final AbstractTestWidget" 1482 " w = new TestWidgetImpl( info );\n final ", 1483 LocationMatcher( '', 18, 24 ), 1484 LocationMatcher( '', 25, 5 ) ), 1485 ), 1486 } ), 1487 has_entries( { 1488 'text': 'Generate toString()...', 1489 'kind': 'source.generate.toString', 1490 'chunks': contains_exactly( 1491 ChunkMatcher( '\n\n@Override\npublic String toString() {' 1492 '\n\treturn "TestFactory []";\n}', 1493 LocationMatcher( '', 32, 4 ), 1494 LocationMatcher( '', 32, 4 ) ), 1495 ), 1496 } ), 1497 ) 1498 } ) 1499 1500 contents = ReadFile( filepath ) 1501 # Wait for jdt.ls to have parsed the file and returned some diagnostics 1502 for tries in range( 0, 60 ): 1503 results = app.post_json( '/event_notification', 1504 BuildRequest( filepath = filepath, 1505 filetype = 'java', 1506 contents = contents, 1507 event_name = 'FileReadyToParse' ) ) 1508 if results.json: 1509 break 1510 1511 time.sleep( .25 ) 1512 1513 with patch( 1514 'ycmd.completers.language_server.language_server_protocol.UriToFilePath', 1515 side_effect = lsp.InvalidUriException ): 1516 RunTest( app, { 1517 'description': 'Invalid URIs do not make us crash', 1518 'request': { 1519 'command': 'FixIt', 1520 'line_num': 27, 1521 'column_num': 12, 1522 'filepath': filepath, 1523 }, 1524 'expect': { 1525 'response': requests.codes.ok, 1526 'data': fixits, 1527 } 1528 } ) 1529 1530 1531@WithRetry 1532@SharedYcmd 1533def Subcommands_Format_WholeFile_Spaces_test( app ): 1534 RunTest( app, { 1535 'description': 'Formatting is applied on the whole file ' 1536 'with tabs composed of 4 spaces', 1537 'request': { 1538 'command': 'Format', 1539 'filepath': TEST_JAVA, 1540 'options': { 1541 'tab_size': 4, 1542 'insert_spaces': True 1543 } 1544 }, 1545 'expect': { 1546 'response': requests.codes.ok, 1547 'data': has_entries( { 1548 'fixits': contains_exactly( has_entries( { 1549 'chunks': contains_exactly( 1550 ChunkMatcher( '\n ', 1551 LocationMatcher( TEST_JAVA, 3, 20 ), 1552 LocationMatcher( TEST_JAVA, 4, 3 ) ), 1553 ChunkMatcher( '\n\n ', 1554 LocationMatcher( TEST_JAVA, 4, 22 ), 1555 LocationMatcher( TEST_JAVA, 6, 3 ) ), 1556 ChunkMatcher( '\n ', 1557 LocationMatcher( TEST_JAVA, 6, 34 ), 1558 LocationMatcher( TEST_JAVA, 7, 5 ) ), 1559 ChunkMatcher( '\n ', 1560 LocationMatcher( TEST_JAVA, 7, 35 ), 1561 LocationMatcher( TEST_JAVA, 8, 5 ) ), 1562 ChunkMatcher( '', 1563 LocationMatcher( TEST_JAVA, 8, 25 ), 1564 LocationMatcher( TEST_JAVA, 8, 26 ) ), 1565 ChunkMatcher( '\n ', 1566 LocationMatcher( TEST_JAVA, 8, 27 ), 1567 LocationMatcher( TEST_JAVA, 9, 3 ) ), 1568 ChunkMatcher( '\n\n ', 1569 LocationMatcher( TEST_JAVA, 9, 4 ), 1570 LocationMatcher( TEST_JAVA, 11, 3 ) ), 1571 ChunkMatcher( '\n ', 1572 LocationMatcher( TEST_JAVA, 11, 29 ), 1573 LocationMatcher( TEST_JAVA, 12, 5 ) ), 1574 ChunkMatcher( '\n ', 1575 LocationMatcher( TEST_JAVA, 12, 26 ), 1576 LocationMatcher( TEST_JAVA, 13, 5 ) ), 1577 ChunkMatcher( '', 1578 LocationMatcher( TEST_JAVA, 13, 24 ), 1579 LocationMatcher( TEST_JAVA, 13, 25 ) ), 1580 ChunkMatcher( '', 1581 LocationMatcher( TEST_JAVA, 13, 29 ), 1582 LocationMatcher( TEST_JAVA, 13, 30 ) ), 1583 ChunkMatcher( '\n\n ', 1584 LocationMatcher( TEST_JAVA, 13, 32 ), 1585 LocationMatcher( TEST_JAVA, 15, 5 ) ), 1586 ChunkMatcher( '\n ', 1587 LocationMatcher( TEST_JAVA, 15, 58 ), 1588 LocationMatcher( TEST_JAVA, 16, 5 ) ), 1589 ChunkMatcher( '\n ', 1590 LocationMatcher( TEST_JAVA, 16, 42 ), 1591 LocationMatcher( TEST_JAVA, 17, 3 ) ), 1592 ChunkMatcher( '\n\n ', 1593 LocationMatcher( TEST_JAVA, 17, 4 ), 1594 LocationMatcher( TEST_JAVA, 20, 3 ) ), 1595 ChunkMatcher( '\n ', 1596 LocationMatcher( TEST_JAVA, 20, 28 ), 1597 LocationMatcher( TEST_JAVA, 21, 5 ) ), 1598 ChunkMatcher( '\n ', 1599 LocationMatcher( TEST_JAVA, 21, 28 ), 1600 LocationMatcher( TEST_JAVA, 22, 5 ) ), 1601 ChunkMatcher( '\n ', 1602 LocationMatcher( TEST_JAVA, 22, 30 ), 1603 LocationMatcher( TEST_JAVA, 23, 5 ) ), 1604 ChunkMatcher( '\n ', 1605 LocationMatcher( TEST_JAVA, 23, 23 ), 1606 LocationMatcher( TEST_JAVA, 24, 5 ) ), 1607 ChunkMatcher( '\n ', 1608 LocationMatcher( TEST_JAVA, 24, 27 ), 1609 LocationMatcher( TEST_JAVA, 25, 3 ) ), 1610 ) 1611 } ) ) 1612 } ) 1613 } 1614 } ) 1615 1616 1617@WithRetry 1618@SharedYcmd 1619def Subcommands_Format_WholeFile_Tabs_test( app ): 1620 RunTest( app, { 1621 'description': 'Formatting is applied on the whole file ' 1622 'with tabs composed of 2 spaces', 1623 'request': { 1624 'command': 'Format', 1625 'filepath': TEST_JAVA, 1626 'options': { 1627 'tab_size': 4, 1628 'insert_spaces': False 1629 } 1630 }, 1631 'expect': { 1632 'response': requests.codes.ok, 1633 'data': has_entries( { 1634 'fixits': contains_exactly( has_entries( { 1635 'chunks': contains_exactly( 1636 ChunkMatcher( '\n\t', 1637 LocationMatcher( TEST_JAVA, 3, 20 ), 1638 LocationMatcher( TEST_JAVA, 4, 3 ) ), 1639 ChunkMatcher( '\n\n\t', 1640 LocationMatcher( TEST_JAVA, 4, 22 ), 1641 LocationMatcher( TEST_JAVA, 6, 3 ) ), 1642 ChunkMatcher( '\n\t\t', 1643 LocationMatcher( TEST_JAVA, 6, 34 ), 1644 LocationMatcher( TEST_JAVA, 7, 5 ) ), 1645 ChunkMatcher( '\n\t\t', 1646 LocationMatcher( TEST_JAVA, 7, 35 ), 1647 LocationMatcher( TEST_JAVA, 8, 5 ) ), 1648 ChunkMatcher( '', 1649 LocationMatcher( TEST_JAVA, 8, 25 ), 1650 LocationMatcher( TEST_JAVA, 8, 26 ) ), 1651 ChunkMatcher( '\n\t', 1652 LocationMatcher( TEST_JAVA, 8, 27 ), 1653 LocationMatcher( TEST_JAVA, 9, 3 ) ), 1654 ChunkMatcher( '\n\n\t', 1655 LocationMatcher( TEST_JAVA, 9, 4 ), 1656 LocationMatcher( TEST_JAVA, 11, 3 ) ), 1657 ChunkMatcher( '\n\t\t', 1658 LocationMatcher( TEST_JAVA, 11, 29 ), 1659 LocationMatcher( TEST_JAVA, 12, 5 ) ), 1660 ChunkMatcher( '\n\t\t', 1661 LocationMatcher( TEST_JAVA, 12, 26 ), 1662 LocationMatcher( TEST_JAVA, 13, 5 ) ), 1663 ChunkMatcher( '', 1664 LocationMatcher( TEST_JAVA, 13, 24 ), 1665 LocationMatcher( TEST_JAVA, 13, 25 ) ), 1666 ChunkMatcher( '', 1667 LocationMatcher( TEST_JAVA, 13, 29 ), 1668 LocationMatcher( TEST_JAVA, 13, 30 ) ), 1669 ChunkMatcher( '\n\n\t\t', 1670 LocationMatcher( TEST_JAVA, 13, 32 ), 1671 LocationMatcher( TEST_JAVA, 15, 5 ) ), 1672 ChunkMatcher( '\n\t\t', 1673 LocationMatcher( TEST_JAVA, 15, 58 ), 1674 LocationMatcher( TEST_JAVA, 16, 5 ) ), 1675 ChunkMatcher( '\n\t', 1676 LocationMatcher( TEST_JAVA, 16, 42 ), 1677 LocationMatcher( TEST_JAVA, 17, 3 ) ), 1678 ChunkMatcher( '\n\n\t', 1679 LocationMatcher( TEST_JAVA, 17, 4 ), 1680 LocationMatcher( TEST_JAVA, 20, 3 ) ), 1681 ChunkMatcher( '\n\t\t', 1682 LocationMatcher( TEST_JAVA, 20, 28 ), 1683 LocationMatcher( TEST_JAVA, 21, 5 ) ), 1684 ChunkMatcher( '\n\t\t', 1685 LocationMatcher( TEST_JAVA, 21, 28 ), 1686 LocationMatcher( TEST_JAVA, 22, 5 ) ), 1687 ChunkMatcher( '\n\t\t', 1688 LocationMatcher( TEST_JAVA, 22, 30 ), 1689 LocationMatcher( TEST_JAVA, 23, 5 ) ), 1690 ChunkMatcher( '\n\t\t', 1691 LocationMatcher( TEST_JAVA, 23, 23 ), 1692 LocationMatcher( TEST_JAVA, 24, 5 ) ), 1693 ChunkMatcher( '\n\t', 1694 LocationMatcher( TEST_JAVA, 24, 27 ), 1695 LocationMatcher( TEST_JAVA, 25, 3 ) ), 1696 ) 1697 } ) ) 1698 } ) 1699 } 1700 } ) 1701 1702 1703@WithRetry 1704@SharedYcmd 1705def Subcommands_Format_Range_Spaces_test( app ): 1706 RunTest( app, { 1707 'description': 'Formatting is applied on some part of the file ' 1708 'with tabs composed of 4 spaces', 1709 'request': { 1710 'command': 'Format', 1711 'filepath': TEST_JAVA, 1712 'range': { 1713 'start': { 1714 'line_num': 20, 1715 'column_num': 1, 1716 }, 1717 'end': { 1718 'line_num': 25, 1719 'column_num': 4 1720 } 1721 }, 1722 'options': { 1723 'tab_size': 4, 1724 'insert_spaces': True 1725 } 1726 }, 1727 'expect': { 1728 'response': requests.codes.ok, 1729 'data': has_entries( { 1730 'fixits': contains_exactly( has_entries( { 1731 'chunks': contains_exactly( 1732 ChunkMatcher( ' ', 1733 LocationMatcher( TEST_JAVA, 20, 1 ), 1734 LocationMatcher( TEST_JAVA, 20, 3 ) ), 1735 ChunkMatcher( '\n ', 1736 LocationMatcher( TEST_JAVA, 20, 28 ), 1737 LocationMatcher( TEST_JAVA, 21, 5 ) ), 1738 ChunkMatcher( '\n ', 1739 LocationMatcher( TEST_JAVA, 21, 28 ), 1740 LocationMatcher( TEST_JAVA, 22, 5 ) ), 1741 ChunkMatcher( '\n ', 1742 LocationMatcher( TEST_JAVA, 22, 30 ), 1743 LocationMatcher( TEST_JAVA, 23, 5 ) ), 1744 ChunkMatcher( '\n ', 1745 LocationMatcher( TEST_JAVA, 23, 23 ), 1746 LocationMatcher( TEST_JAVA, 24, 5 ) ), 1747 ) 1748 } ) ) 1749 } ) 1750 } 1751 } ) 1752 1753 1754@WithRetry 1755@SharedYcmd 1756def Subcommands_Format_Range_Tabs_test( app ): 1757 RunTest( app, { 1758 'description': 'Formatting is applied on some part of the file ' 1759 'with tabs instead of spaces', 1760 'request': { 1761 'command': 'Format', 1762 'filepath': TEST_JAVA, 1763 'range': { 1764 'start': { 1765 'line_num': 20, 1766 'column_num': 1, 1767 }, 1768 'end': { 1769 'line_num': 25, 1770 'column_num': 4 1771 } 1772 }, 1773 'options': { 1774 'tab_size': 4, 1775 'insert_spaces': False 1776 } 1777 }, 1778 'expect': { 1779 'response': requests.codes.ok, 1780 'data': has_entries( { 1781 'fixits': contains_exactly( has_entries( { 1782 'chunks': contains_exactly( 1783 ChunkMatcher( '\t', 1784 LocationMatcher( TEST_JAVA, 20, 1 ), 1785 LocationMatcher( TEST_JAVA, 20, 3 ) ), 1786 ChunkMatcher( '\n\t\t', 1787 LocationMatcher( TEST_JAVA, 20, 28 ), 1788 LocationMatcher( TEST_JAVA, 21, 5 ) ), 1789 ChunkMatcher( '\n\t\t', 1790 LocationMatcher( TEST_JAVA, 21, 28 ), 1791 LocationMatcher( TEST_JAVA, 22, 5 ) ), 1792 ChunkMatcher( '\n\t\t', 1793 LocationMatcher( TEST_JAVA, 22, 30 ), 1794 LocationMatcher( TEST_JAVA, 23, 5 ) ), 1795 ChunkMatcher( '\n\t\t', 1796 LocationMatcher( TEST_JAVA, 23, 23 ), 1797 LocationMatcher( TEST_JAVA, 24, 5 ) ), 1798 ChunkMatcher( '\n\t', 1799 LocationMatcher( TEST_JAVA, 24, 27 ), 1800 LocationMatcher( TEST_JAVA, 25, 3 ) ), 1801 ) 1802 } ) ) 1803 } ) 1804 } 1805 } ) 1806 1807 1808@WithRetry 1809@SharedYcmd 1810def RunGoToTest( app, description, filepath, line, col, cmd, goto_response ): 1811 RunTest( app, { 1812 'description': description, 1813 'request': { 1814 'command': cmd, 1815 'line_num': line, 1816 'column_num': col, 1817 'filepath': filepath 1818 }, 1819 'expect': { 1820 'response': requests.codes.ok, 1821 'data': goto_response, 1822 } 1823 } ) 1824 1825 1826@pytest.mark.parametrize( 'test', [ 1827 # Member function local variable 1828 { 'request': { 'line': 28, 'col': 5, 'filepath': TESTLAUNCHER_JAVA }, 1829 'response': { 'line_num': 27, 'column_num': 18, 1830 'filepath': TESTLAUNCHER_JAVA }, 1831 'description': 'GoTo works for member local variable' }, 1832 # Member variable 1833 { 'request': { 'line': 22, 'col': 7, 'filepath': TESTLAUNCHER_JAVA }, 1834 'response': { 'line_num': 8, 'column_num': 16, 1835 'filepath': TESTLAUNCHER_JAVA }, 1836 'description': 'GoTo works for member variable' }, 1837 # Method 1838 { 'request': { 'line': 28, 'col': 7, 'filepath': TESTLAUNCHER_JAVA }, 1839 'response': { 'line_num': 21, 'column_num': 16, 1840 'filepath': TESTLAUNCHER_JAVA }, 1841 'description': 'GoTo works for method' }, 1842 # Constructor 1843 { 'request': { 'line': 38, 'col': 26, 'filepath': TESTLAUNCHER_JAVA }, 1844 'response': { 'line_num': 10, 'column_num': 10, 1845 'filepath': TESTLAUNCHER_JAVA }, 1846 'description': 'GoTo works for jumping to constructor' }, 1847 # Jump to self - main() 1848 { 'request': { 'line': 26, 'col': 22, 'filepath': TESTLAUNCHER_JAVA }, 1849 'response': { 'line_num': 26, 'column_num': 22, 1850 'filepath': TESTLAUNCHER_JAVA }, 1851 'description': 'GoTo works for jumping to the same position' }, 1852 # Static method 1853 { 'request': { 'line': 37, 'col': 11, 'filepath': TESTLAUNCHER_JAVA }, 1854 'response': { 'line_num': 13, 'column_num': 21, 1855 'filepath': TESTLAUNCHER_JAVA }, 1856 'description': 'GoTo works for static method' }, 1857 # Static variable 1858 { 'request': { 'line': 14, 'col': 11, 'filepath': TESTLAUNCHER_JAVA }, 1859 'response': { 'line_num': 12, 'column_num': 21, 1860 'filepath': TESTLAUNCHER_JAVA }, 1861 'description': 'GoTo works for static variable' }, 1862 # Argument variable 1863 { 'request': { 'line': 23, 'col': 5, 'filepath': TESTLAUNCHER_JAVA }, 1864 'response': { 'line_num': 21, 'column_num': 32, 1865 'filepath': TESTLAUNCHER_JAVA }, 1866 'description': 'GoTo works for argument variable' }, 1867 # Class 1868 { 'request': { 'line': 27, 'col': 10, 'filepath': TESTLAUNCHER_JAVA }, 1869 'response': { 'line_num': 6, 'column_num': 7, 1870 'filepath': TESTLAUNCHER_JAVA }, 1871 'description': 'GoTo works for jumping to class declaration' }, 1872 # Unicode 1873 { 'request': { 'line': 8, 'col': 12, 'filepath': TEST_JAVA }, 1874 'response': { 'line_num': 7, 'column_num': 12, 'filepath': TEST_JAVA }, 1875 'description': 'GoTo works for unicode identifiers' } 1876 ] ) 1877@pytest.mark.parametrize( 'command', [ 'GoTo', 1878 'GoToDefinition', 1879 'GoToDeclaration' ] ) 1880@SharedYcmd 1881def Subcommands_GoTo_test( app, command, test ): 1882 RunGoToTest( app, 1883 test[ 'description' ], 1884 test[ 'request' ][ 'filepath' ], 1885 test[ 'request' ][ 'line' ], 1886 test[ 'request' ][ 'col' ], 1887 command, 1888 has_entries( test[ 'response' ] ) ) 1889 1890 1891@pytest.mark.parametrize( 'test', [ 1892 # Member function local variable 1893 { 'request': { 'line': 28, 'col': 5, 'filepath': TESTLAUNCHER_JAVA }, 1894 'response': { 'line_num': 6, 'column_num': 7, 1895 'filepath': TESTLAUNCHER_JAVA }, 1896 'description': 'GoToType works for member local variable' }, 1897 # Member variable 1898 { 'request': { 'line': 22, 'col': 7, 'filepath': TESTLAUNCHER_JAVA }, 1899 'response': { 'line_num': 6, 'column_num': 14, 'filepath': TSET_JAVA }, 1900 'description': 'GoToType works for member variable' }, 1901 ] ) 1902@SharedYcmd 1903def Subcommands_GoToType_test( app, test ): 1904 RunGoToTest( app, 1905 test[ 'description' ], 1906 test[ 'request' ][ 'filepath' ], 1907 test[ 'request' ][ 'line' ], 1908 test[ 'request' ][ 'col' ], 1909 'GoToType', 1910 has_entries( test[ 'response' ] ) ) 1911 1912 1913@pytest.mark.parametrize( 'test', [ 1914 # Interface 1915 { 'request': { 'line': 17, 'col': 25, 'filepath': TESTLAUNCHER_JAVA }, 1916 'response': { 'line_num': 28, 'column_num': 16, 1917 'filepath': TESTLAUNCHER_JAVA }, 1918 'description': 'GoToImplementation on interface ' 1919 'jumps to its implementation' }, 1920 # Interface reference 1921 { 'request': { 'line': 21, 'col': 30, 'filepath': TESTLAUNCHER_JAVA }, 1922 'response': { 'line_num': 28, 'column_num': 16, 1923 'filepath': TESTLAUNCHER_JAVA }, 1924 'description': 'GoToImplementation on interface reference ' 1925 'jumpts to its implementation' }, 1926 ] ) 1927@SharedYcmd 1928def Subcommands_GoToImplementation_test( app, test ): 1929 RunGoToTest( app, 1930 test[ 'description' ], 1931 test[ 'request' ][ 'filepath' ], 1932 test[ 'request' ][ 'line' ], 1933 test[ 'request' ][ 'col' ], 1934 'GoToImplementation', 1935 has_entries( test[ 'response' ] ) ) 1936 1937 1938@WithRetry 1939@SharedYcmd 1940def Subcommands_OrganizeImports_test( app ): 1941 RunTest( app, { 1942 'description': 'Imports are resolved and sorted, ' 1943 'and unused ones are removed', 1944 'request': { 1945 'command': 'OrganizeImports', 1946 'filepath': TESTLAUNCHER_JAVA 1947 }, 1948 'expect': { 1949 'response': requests.codes.ok, 1950 'data': has_entries( { 1951 'fixits': contains_exactly( has_entries( { 1952 'chunks': contains_exactly( 1953 ChunkMatcher( 'import com.youcompleteme.Test;\n' 1954 'import com.youcompleteme.testing.Tset;', 1955 LocationMatcher( TESTLAUNCHER_JAVA, 3, 1 ), 1956 LocationMatcher( TESTLAUNCHER_JAVA, 4, 54 ) ), 1957 ) 1958 } ) ) 1959 } ) 1960 } 1961 } ) 1962 1963 1964@WithRetry 1965@SharedYcmd 1966@patch( 'ycmd.completers.language_server.language_server_completer.' 1967 'REQUEST_TIMEOUT_COMMAND', 1968 5 ) 1969def Subcommands_RequestTimeout_test( app ): 1970 with patch.object( 1971 handlers._server_state.GetFiletypeCompleter( [ 'java' ] ).GetConnection(), 1972 'WriteData' ): 1973 RunTest( app, { 1974 'description': 'Request timeout throws an error', 1975 'request': { 1976 'command': 'FixIt', 1977 'line_num': 1, 1978 'column_num': 1, 1979 'filepath': TEST_JAVA, 1980 }, 1981 'expect': { 1982 'response': requests.codes.internal_server_error, 1983 'data': ErrorMatcher( ResponseTimeoutException, 'Response Timeout' ) 1984 } 1985 } ) 1986 1987 1988@WithRetry 1989@SharedYcmd 1990def Subcommands_RequestFailed_test( app ): 1991 connection = handlers._server_state.GetFiletypeCompleter( 1992 [ 'java' ] ).GetConnection() 1993 1994 def WriteJunkToServer( data ): 1995 junk = data.replace( bytes( b'textDocument/codeAction' ), 1996 bytes( b'textDocument/codeFAILED' ) ) 1997 1998 with connection._stdin_lock: 1999 connection._server_stdin.write( junk ) 2000 connection._server_stdin.flush() 2001 2002 2003 with patch.object( connection, 'WriteData', side_effect = WriteJunkToServer ): 2004 RunTest( app, { 2005 'description': 'Response errors propagate to the client', 2006 'request': { 2007 'command': 'FixIt', 2008 'line_num': 1, 2009 'column_num': 1, 2010 'filepath': TEST_JAVA, 2011 }, 2012 'expect': { 2013 'response': requests.codes.internal_server_error, 2014 'data': ErrorMatcher( ResponseFailedException ) 2015 } 2016 } ) 2017 2018 2019@WithRetry 2020@SharedYcmd 2021def Subcommands_IndexOutOfRange_test( app ): 2022 RunTest( app, { 2023 'description': 'Request with invalid position does not crash', 2024 'request': { 2025 'command': 'FixIt', 2026 'line_num': 99, 2027 'column_num': 99, 2028 'filepath': TEST_JAVA, 2029 }, 2030 'expect': { 2031 'response': requests.codes.ok, 2032 'data': has_entries( { 'fixits': contains_exactly( 2033 has_entries( { 'text': 'Generate Getters and Setters', 2034 'chunks': instance_of( list ) } ), 2035 has_entries( { 'text': 'Change modifiers to final where possible', 2036 'chunks': instance_of( list ) } ), 2037 ) } ), 2038 } 2039 } ) 2040 2041 2042@WithRetry 2043@SharedYcmd 2044def Subcommands_InvalidRange_test( app ): 2045 RunTest( app, { 2046 'description': 'Request with invalid visual range is rejected', 2047 'request': { 2048 'command': 'FixIt', 2049 'line_num': 99, 2050 'column_num': 99, 2051 'filepath': TEST_JAVA, 2052 'range': { 2053 'start': { 2054 'line_num': 99, 2055 'column_num': 99 2056 }, 2057 'end': { 2058 'line_num': 100, 2059 'column_num': 100 2060 } 2061 } 2062 }, 2063 'expect': { 2064 'response': requests.codes.internal_server_error, 2065 'data': ErrorMatcher( RuntimeError, 'Invalid range' ), 2066 } 2067 } ) 2068 2069 2070@WithRetry 2071@SharedYcmd 2072def Subcommands_DifferentFileTypesUpdate_test( app ): 2073 RunTest( app, { 2074 'description': 'Request error handles the error', 2075 'request': { 2076 'command': 'FixIt', 2077 'line_num': 99, 2078 'column_num': 99, 2079 'filepath': TEST_JAVA, 2080 'file_data': { 2081 '!/bin/sh': { 2082 'filetypes': [], 2083 'contents': 'this should be ignored by the completer', 2084 }, 2085 '/path/to/non/project/file': { 2086 'filetypes': [ 'c' ], 2087 'contents': 'this should be ignored by the completer', 2088 }, 2089 TESTLAUNCHER_JAVA: { 2090 'filetypes': [ 'some', 'java', 'junk', 'also' ], 2091 'contents': ReadFile( TESTLAUNCHER_JAVA ), 2092 }, 2093 '!/usr/bin/sh': { 2094 'filetypes': [ 'java' ], 2095 'contents': '\n', 2096 }, 2097 } 2098 }, 2099 'expect': { 2100 'response': requests.codes.ok, 2101 'data': has_entries( { 'fixits': contains_exactly( 2102 has_entries( { 'text': 'Generate Getters and Setters', 2103 'chunks': instance_of( list ) } ), 2104 has_entries( { 'text': 'Change modifiers to final where possible', 2105 'chunks': instance_of( list ) } ), 2106 ) } ), 2107 } 2108 } ) 2109 2110 2111@WithRetry 2112@IsolatedYcmd( { 'extra_conf_globlist': 2113 PathToTestFile( 'extra_confs', '*' ) } ) 2114def Subcommands_ExtraConf_SettingsValid_test( app ): 2115 filepath = PathToTestFile( 'extra_confs', 2116 'simple_extra_conf_project', 2117 'src', 2118 'ExtraConf.java' ) 2119 RunTest( app, { 2120 'description': 'RefactorRename is disabled in extra conf.', 2121 'request': { 2122 'command': 'RefactorRename', 2123 'arguments': [ 'renamed_l' ], 2124 'filepath': filepath, 2125 'line_num': 1, 2126 'column_num': 7, 2127 }, 2128 'expect': { 2129 'response': requests.codes.ok, 2130 'data': has_entries( { 2131 'fixits': contains_exactly( has_entries( { 2132 'chunks': empty(), 2133 'location': LocationMatcher( filepath, 1, 7 ) 2134 } ) ) 2135 } ) 2136 } 2137 } ) 2138 2139 2140@WithRetry 2141@IsolatedYcmd( { 'extra_conf_globlist': 2142 PathToTestFile( 'extra_confs', '*' ) } ) 2143def Subcommands_AdditionalFormatterOptions_test( app ): 2144 filepath = PathToTestFile( 'extra_confs', 2145 'simple_extra_conf_project', 2146 'src', 2147 'ExtraConf.java' ) 2148 RunTest( app, { 2149 'description': 'Format respects settings from extra conf.', 2150 'request': { 2151 'command': 'Format', 2152 'filepath': filepath, 2153 'options': { 2154 'tab_size': 4, 2155 'insert_spaces': True 2156 } 2157 }, 2158 'expect': { 2159 'response': requests.codes.ok, 2160 'data': has_entries( { 2161 'fixits': contains_exactly( has_entries( { 2162 'chunks': contains_exactly( 2163 ChunkMatcher( '\n ', 2164 LocationMatcher( filepath, 1, 18 ), 2165 LocationMatcher( filepath, 2, 3 ) ), 2166 ChunkMatcher( '\n ', 2167 LocationMatcher( filepath, 2, 20 ), 2168 LocationMatcher( filepath, 2, 21 ) ), 2169 ChunkMatcher( '', 2170 LocationMatcher( filepath, 2, 29 ), 2171 LocationMatcher( filepath, 2, 30 ) ), 2172 ChunkMatcher( '\n ', 2173 LocationMatcher( filepath, 2, 33 ), 2174 LocationMatcher( filepath, 2, 33 ) ), 2175 ChunkMatcher( '\n\n ', 2176 LocationMatcher( filepath, 2, 34 ), 2177 LocationMatcher( filepath, 4, 3 ) ), 2178 ChunkMatcher( '\n ', 2179 LocationMatcher( filepath, 4, 27 ), 2180 LocationMatcher( filepath, 4, 28 ) ), 2181 ChunkMatcher( '', 2182 LocationMatcher( filepath, 4, 41 ), 2183 LocationMatcher( filepath, 4, 42 ) ), 2184 ChunkMatcher( '\n ', 2185 LocationMatcher( filepath, 4, 45 ), 2186 LocationMatcher( filepath, 5, 5 ) ), 2187 ChunkMatcher( '\n ', 2188 LocationMatcher( filepath, 5, 33 ), 2189 LocationMatcher( filepath, 5, 34 ) ), 2190 ChunkMatcher( '', 2191 LocationMatcher( filepath, 5, 36 ), 2192 LocationMatcher( filepath, 5, 37 ) ), 2193 ChunkMatcher( '\n ', 2194 LocationMatcher( filepath, 5, 39 ), 2195 LocationMatcher( filepath, 6, 5 ) ), 2196 ChunkMatcher( '\n ', 2197 LocationMatcher( filepath, 6, 33 ), 2198 LocationMatcher( filepath, 6, 34 ) ), 2199 ChunkMatcher( '', 2200 LocationMatcher( filepath, 6, 35 ), 2201 LocationMatcher( filepath, 6, 36 ) ), 2202 ChunkMatcher( '\n ', 2203 LocationMatcher( filepath, 6, 38 ), 2204 LocationMatcher( filepath, 7, 5 ) ), 2205 ChunkMatcher( '\n ', 2206 LocationMatcher( filepath, 7, 11 ), 2207 LocationMatcher( filepath, 8, 5 ) ), 2208 ChunkMatcher( '\n ', 2209 LocationMatcher( filepath, 8, 11 ), 2210 LocationMatcher( filepath, 9, 3 ) ), 2211 ), 2212 'location': LocationMatcher( filepath, 1, 1 ) 2213 } ) ) 2214 } ) 2215 } 2216 } ) 2217 2218 2219@WithRetry 2220@IsolatedYcmd() 2221def Subcommands_ExtraConf_SettingsValid_UnknownExtraConf_test( app ): 2222 filepath = PathToTestFile( 'extra_confs', 2223 'simple_extra_conf_project', 2224 'src', 2225 'ExtraConf.java' ) 2226 contents = ReadFile( filepath ) 2227 2228 response = app.post_json( '/event_notification', 2229 BuildRequest( **{ 2230 'event_name': 'FileReadyToParse', 2231 'contents': contents, 2232 'filepath': filepath, 2233 'line_num': 1, 2234 'column_num': 7, 2235 'filetype': 'java', 2236 } ), 2237 expect_errors = True ) 2238 2239 print( 'FileReadyToParse result: ' 2240 f'{ json.dumps( response.json, indent = 2 ) }' ) 2241 2242 assert_that( response.status_code, 2243 equal_to( requests.codes.internal_server_error ) ) 2244 assert_that( response.json, ErrorMatcher( UnknownExtraConf ) ) 2245 2246 app.post_json( 2247 '/ignore_extra_conf_file', 2248 { 'filepath': PathToTestFile( 'extra_confs', '.ycm_extra_conf.py' ) } ) 2249 2250 RunTest( app, { 2251 'description': 'RefactorRename is disabled in extra conf but ignored.', 2252 'request': { 2253 'command': 'RefactorRename', 2254 'arguments': [ 'renamed_l' ], 2255 'filepath': filepath, 2256 'contents': contents, 2257 'line_num': 1, 2258 'column_num': 7, 2259 }, 2260 'expect': { 2261 'response': requests.codes.ok, 2262 'data': has_entries( { 2263 'fixits': contains_exactly( has_entries( { 2264 # Just prove that we actually got a reasonable result 2265 'chunks': is_not( empty() ), 2266 } ) ) 2267 } ) 2268 } 2269 } ) 2270 2271 2272@SharedYcmd 2273def Subcommands_ExecuteCommand_NoArguments_test( app ): 2274 RunTest( app, { 2275 'description': 'Running a command without args fails', 2276 'request': { 2277 'command': 'ExecuteCommand', 2278 'line_num': 1, 2279 'column_num': 1, 2280 'filepath': TEST_JAVA, 2281 }, 2282 'expect': { 2283 'response': requests.codes.internal_server_error, 2284 'data': ErrorMatcher( ValueError, 2285 'Must specify a command to execute' ), 2286 } 2287 } ) 2288 2289 2290@SharedYcmd 2291def Subcommands_ExecuteCommand_test( app ): 2292 RunTest( app, { 2293 'description': 'Running a command does what it says it does', 2294 'request': { 2295 'command': 'ExecuteCommand', 2296 'arguments': [ 'java.edit.organizeImports' ], 2297 'line_num': 1, 2298 'column_num': 1, 2299 'filepath': TEST_JAVA, 2300 }, 2301 'expect': { 2302 # We don't specify the path for import organize, and jdt.ls returns shrug 2303 'response': requests.codes.ok, 2304 'data': '' 2305 } 2306 } ) 2307 2308 2309def Dummy_test(): 2310 # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 2311 assert True 2312