github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/internal/imports/fix_test.go (about) 1 // Copyright 2013 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package imports 6 7 import ( 8 "context" 9 "flag" 10 "fmt" 11 "go/build" 12 "io/ioutil" 13 "log" 14 "path/filepath" 15 "reflect" 16 "sort" 17 "strings" 18 "sync" 19 "testing" 20 21 "github.com/powerman/golang-tools/go/packages/packagestest" 22 "github.com/powerman/golang-tools/internal/gocommand" 23 ) 24 25 var testDebug = flag.Bool("debug", false, "enable debug output") 26 27 var tests = []struct { 28 name string 29 formatOnly bool 30 in, out string 31 }{ 32 // Adding an import to an existing parenthesized import 33 { 34 name: "factored_imports_add", 35 in: `package foo 36 import ( 37 "fmt" 38 ) 39 func bar() { 40 var b bytes.Buffer 41 fmt.Println(b.String()) 42 } 43 `, 44 out: `package foo 45 46 import ( 47 "bytes" 48 "fmt" 49 ) 50 51 func bar() { 52 var b bytes.Buffer 53 fmt.Println(b.String()) 54 } 55 `, 56 }, 57 58 // Adding an import to an existing parenthesized import, 59 // verifying it goes into the first section. 60 { 61 name: "factored_imports_add_first_sec", 62 in: `package foo 63 import ( 64 "fmt" 65 66 "github.com/golang/snappy" 67 ) 68 func bar() { 69 var b bytes.Buffer 70 _ = snappy.ErrCorrupt 71 fmt.Println(b.String()) 72 } 73 `, 74 out: `package foo 75 76 import ( 77 "bytes" 78 "fmt" 79 80 "github.com/golang/snappy" 81 ) 82 83 func bar() { 84 var b bytes.Buffer 85 _ = snappy.ErrCorrupt 86 fmt.Println(b.String()) 87 } 88 `, 89 }, 90 91 // Adding an import to an existing parenthesized import, 92 // verifying it goes into the first section. (test 2) 93 { 94 name: "factored_imports_add_first_sec_2", 95 in: `package foo 96 import ( 97 "fmt" 98 99 "github.com/golang/snappy" 100 ) 101 func bar() { 102 _ = math.NaN 103 _ = fmt.Sprintf 104 _ = snappy.ErrCorrupt 105 } 106 `, 107 out: `package foo 108 109 import ( 110 "fmt" 111 "math" 112 113 "github.com/golang/snappy" 114 ) 115 116 func bar() { 117 _ = math.NaN 118 _ = fmt.Sprintf 119 _ = snappy.ErrCorrupt 120 } 121 `, 122 }, 123 124 // Adding a new import line, without parens 125 { 126 name: "add_import_section", 127 in: `package foo 128 func bar() { 129 var b bytes.Buffer 130 } 131 `, 132 out: `package foo 133 134 import "bytes" 135 136 func bar() { 137 var b bytes.Buffer 138 } 139 `, 140 }, 141 142 // Adding two new imports, which should make a parenthesized import decl. 143 { 144 name: "add_import_paren_section", 145 in: `package foo 146 func bar() { 147 _, _ := bytes.Buffer, zip.NewReader 148 } 149 `, 150 out: `package foo 151 152 import ( 153 "archive/zip" 154 "bytes" 155 ) 156 157 func bar() { 158 _, _ := bytes.Buffer, zip.NewReader 159 } 160 `, 161 }, 162 163 // Make sure we don't add things twice 164 { 165 name: "no_double_add", 166 in: `package foo 167 func bar() { 168 _, _ := bytes.Buffer, bytes.NewReader 169 } 170 `, 171 out: `package foo 172 173 import "bytes" 174 175 func bar() { 176 _, _ := bytes.Buffer, bytes.NewReader 177 } 178 `, 179 }, 180 181 // Make sure we don't add packages that don't have the right exports 182 { 183 name: "no_mismatched_add", 184 in: `package foo 185 186 func bar() { 187 _ := bytes.NonexistentSymbol 188 } 189 `, 190 out: `package foo 191 192 func bar() { 193 _ := bytes.NonexistentSymbol 194 } 195 `, 196 }, 197 198 // Remove unused imports, 1 of a factored block 199 { 200 name: "remove_unused_1_of_2", 201 in: `package foo 202 import ( 203 "bytes" 204 "fmt" 205 ) 206 207 func bar() { 208 _, _ := bytes.Buffer, bytes.NewReader 209 } 210 `, 211 out: `package foo 212 213 import ( 214 "bytes" 215 ) 216 217 func bar() { 218 _, _ := bytes.Buffer, bytes.NewReader 219 } 220 `, 221 }, 222 223 // Remove unused imports, 2 of 2 224 { 225 name: "remove_unused_2_of_2", 226 in: `package foo 227 import ( 228 "bytes" 229 "fmt" 230 ) 231 232 func bar() { 233 } 234 `, 235 out: `package foo 236 237 func bar() { 238 } 239 `, 240 }, 241 242 // Remove unused imports, 1 of 1 243 { 244 name: "remove_unused_1_of_1", 245 in: `package foo 246 247 import "fmt" 248 249 func bar() { 250 } 251 `, 252 out: `package foo 253 254 func bar() { 255 } 256 `, 257 }, 258 259 // Don't remove empty imports. 260 { 261 name: "dont_remove_empty_imports", 262 in: `package foo 263 import ( 264 _ "image/png" 265 _ "image/jpeg" 266 ) 267 `, 268 out: `package foo 269 270 import ( 271 _ "image/jpeg" 272 _ "image/png" 273 ) 274 `, 275 }, 276 277 // Don't remove dot imports. 278 { 279 name: "dont_remove_dot_imports", 280 in: `package foo 281 import ( 282 . "foo" 283 . "bar" 284 ) 285 `, 286 out: `package foo 287 288 import ( 289 . "bar" 290 . "foo" 291 ) 292 `, 293 }, 294 295 // Skip refs the parser can resolve. 296 { 297 name: "skip_resolved_refs", 298 in: `package foo 299 300 func f() { 301 type t struct{ Println func(string) } 302 fmt := t{Println: func(string) {}} 303 fmt.Println("foo") 304 } 305 `, 306 out: `package foo 307 308 func f() { 309 type t struct{ Println func(string) } 310 fmt := t{Println: func(string) {}} 311 fmt.Println("foo") 312 } 313 `, 314 }, 315 316 // Do not add a package we already have a resolution for. 317 { 318 name: "skip_template", 319 in: `package foo 320 321 import "html/template" 322 323 func f() { t = template.New("sometemplate") } 324 `, 325 out: `package foo 326 327 import "html/template" 328 329 func f() { t = template.New("sometemplate") } 330 `, 331 }, 332 333 // Don't touch cgo 334 { 335 name: "cgo", 336 in: `package foo 337 338 /* 339 #include <foo.h> 340 */ 341 import "C" 342 `, 343 out: `package foo 344 345 /* 346 #include <foo.h> 347 */ 348 import "C" 349 `, 350 }, 351 352 // Put some things in their own section 353 { 354 name: "make_sections", 355 in: `package foo 356 357 import ( 358 "os" 359 ) 360 361 func foo () { 362 _, _ = os.Args, fmt.Println 363 _, _ = snappy.ErrCorrupt, p.P 364 } 365 `, 366 out: `package foo 367 368 import ( 369 "fmt" 370 "os" 371 372 "github.com/golang/snappy" 373 "rsc.io/p" 374 ) 375 376 func foo() { 377 _, _ = os.Args, fmt.Println 378 _, _ = snappy.ErrCorrupt, p.P 379 } 380 `, 381 }, 382 // Merge import blocks, even when no additions are required. 383 { 384 name: "merge_import_blocks_no_fix", 385 in: `package foo 386 387 import ( 388 "fmt" 389 ) 390 import "os" 391 392 import ( 393 "rsc.io/p" 394 ) 395 396 var _, _ = os.Args, fmt.Println 397 var _, _ = snappy.ErrCorrupt, p.P 398 `, 399 out: `package foo 400 401 import ( 402 "fmt" 403 "os" 404 405 "github.com/golang/snappy" 406 "rsc.io/p" 407 ) 408 409 var _, _ = os.Args, fmt.Println 410 var _, _ = snappy.ErrCorrupt, p.P 411 `, 412 }, 413 // Delete existing empty import block 414 { 415 name: "delete_empty_import_block", 416 in: `package foo 417 418 import () 419 `, 420 out: `package foo 421 `, 422 }, 423 424 // Use existing empty import block 425 { 426 name: "use_empty_import_block", 427 in: `package foo 428 429 import () 430 431 func f() { 432 _ = fmt.Println 433 } 434 `, 435 out: `package foo 436 437 import "fmt" 438 439 func f() { 440 _ = fmt.Println 441 } 442 `, 443 }, 444 445 // Blank line before adding new section. 446 { 447 name: "blank_line_before_new_group", 448 in: `package foo 449 450 import ( 451 "fmt" 452 "net" 453 ) 454 455 func f() { 456 _ = net.Dial 457 _ = fmt.Printf 458 _ = snappy.ErrCorrupt 459 } 460 `, 461 out: `package foo 462 463 import ( 464 "fmt" 465 "net" 466 467 "github.com/golang/snappy" 468 ) 469 470 func f() { 471 _ = net.Dial 472 _ = fmt.Printf 473 _ = snappy.ErrCorrupt 474 } 475 `, 476 }, 477 478 // Blank line between standard library and third-party stuff. 479 { 480 name: "blank_line_separating_std_and_third_party", 481 in: `package foo 482 483 import ( 484 "github.com/golang/snappy" 485 "fmt" 486 "net" 487 ) 488 489 func f() { 490 _ = net.Dial 491 _ = fmt.Printf 492 _ = snappy.Foo 493 } 494 `, 495 out: `package foo 496 497 import ( 498 "fmt" 499 "net" 500 501 "github.com/golang/snappy" 502 ) 503 504 func f() { 505 _ = net.Dial 506 _ = fmt.Printf 507 _ = snappy.Foo 508 } 509 `, 510 }, 511 512 // golang.org/issue/6884 513 { 514 name: "new_imports_before_comment", 515 in: `package main 516 517 // A comment 518 func main() { 519 fmt.Println("Hello, world") 520 } 521 `, 522 out: `package main 523 524 import "fmt" 525 526 // A comment 527 func main() { 528 fmt.Println("Hello, world") 529 } 530 `, 531 }, 532 533 // golang.org/issue/7132 534 { 535 name: "new_section_for_dotless_import", 536 in: `package main 537 538 import ( 539 "fmt" 540 541 "gu" 542 "manypackages.com/packagea" 543 ) 544 545 var ( 546 a = packagea.A 547 b = gu.A 548 c = fmt.Printf 549 ) 550 `, 551 out: `package main 552 553 import ( 554 "fmt" 555 556 "gu" 557 558 "manypackages.com/packagea" 559 ) 560 561 var ( 562 a = packagea.A 563 b = gu.A 564 c = fmt.Printf 565 ) 566 `, 567 }, 568 569 { 570 name: "fragment_with_main", 571 in: `func main(){fmt.Println("Hello, world")}`, 572 out: `package main 573 574 import "fmt" 575 576 func main() { fmt.Println("Hello, world") } 577 `, 578 }, 579 580 { 581 name: "fragment_without_main", 582 in: `func notmain(){fmt.Println("Hello, world")}`, 583 out: `import "fmt" 584 585 func notmain() { fmt.Println("Hello, world") }`, 586 }, 587 588 // Remove first import within in a 2nd/3rd/4th/etc. section. 589 // golang.org/issue/7679 590 { 591 name: "remove_first_import_in_section", 592 in: `package main 593 594 import ( 595 "fmt" 596 597 "manypackages.com/packagea" 598 "manypackages.com/packageb" 599 ) 600 601 func main() { 602 var _ = fmt.Println 603 //var _ = packagea.A 604 var _ = packageb.B 605 } 606 `, 607 out: `package main 608 609 import ( 610 "fmt" 611 612 "manypackages.com/packageb" 613 ) 614 615 func main() { 616 var _ = fmt.Println 617 //var _ = packagea.A 618 var _ = packageb.B 619 } 620 `, 621 }, 622 623 // Blank line can be added before all types of import declarations. 624 // golang.org/issue/7866 625 { 626 name: "new_section_for_all_kinds_of_imports", 627 in: `package main 628 629 import ( 630 "fmt" 631 renamed_packagea "manypackages.com/packagea" 632 633 . "manypackages.com/packageb" 634 "io" 635 636 _ "manypackages.com/packagec" 637 "strings" 638 ) 639 640 var _, _, _, _, _ = fmt.Errorf, io.Copy, strings.Contains, renamed_packagea.A, B 641 `, 642 out: `package main 643 644 import ( 645 "fmt" 646 647 renamed_packagea "manypackages.com/packagea" 648 649 "io" 650 651 . "manypackages.com/packageb" 652 653 "strings" 654 655 _ "manypackages.com/packagec" 656 ) 657 658 var _, _, _, _, _ = fmt.Errorf, io.Copy, strings.Contains, renamed_packagea.A, B 659 `, 660 }, 661 662 // Blank line can be added even when first import of group has comment with quote 663 { 664 name: "new_section_where_trailing_comment_has_quote", 665 in: `package main 666 667 import ( 668 "context" 669 bar "local.com/bar" 670 baz "local.com/baz" 671 buzz "local.com/buzz" 672 "github.com/golang/snappy" // this is a "typical" import 673 ) 674 675 var _, _, _, _, _ = context.Background, bar.B, baz.B, buzz.B, snappy.ErrCorrupt 676 `, 677 out: `package main 678 679 import ( 680 "context" 681 682 "github.com/golang/snappy" // this is a "typical" import 683 684 bar "local.com/bar" 685 baz "local.com/baz" 686 buzz "local.com/buzz" 687 ) 688 689 var _, _, _, _, _ = context.Background, bar.B, baz.B, buzz.B, snappy.ErrCorrupt 690 `, 691 }, 692 693 // Non-idempotent comment formatting 694 // golang.org/issue/8035 695 { 696 name: "comments_formatted", 697 in: `package main 698 699 import ( 700 "fmt" // A 701 "go/ast" // B 702 _ "manypackages.com/packagec" // C 703 ) 704 705 func main() { _, _ = fmt.Print, ast.Walk } 706 `, 707 out: `package main 708 709 import ( 710 "fmt" // A 711 "go/ast" // B 712 713 _ "manypackages.com/packagec" // C 714 ) 715 716 func main() { _, _ = fmt.Print, ast.Walk } 717 `, 718 }, 719 720 // Failure to delete all duplicate imports 721 // golang.org/issue/8459 722 { 723 name: "remove_duplicates", 724 in: `package main 725 726 import ( 727 "fmt" 728 "log" 729 "log" 730 "math" 731 ) 732 733 func main() { fmt.Println("pi:", math.Pi) } 734 `, 735 out: `package main 736 737 import ( 738 "fmt" 739 "math" 740 ) 741 742 func main() { fmt.Println("pi:", math.Pi) } 743 `, 744 }, 745 746 // Too aggressive prefix matching 747 // golang.org/issue/9961 748 { 749 name: "no_extra_groups", 750 in: `package p 751 752 import ( 753 "zip" 754 755 "rsc.io/p" 756 ) 757 758 var ( 759 _ = fmt.Print 760 _ = zip.Store 761 _ p.P 762 _ = regexp.Compile 763 ) 764 `, 765 out: `package p 766 767 import ( 768 "fmt" 769 "regexp" 770 "zip" 771 772 "rsc.io/p" 773 ) 774 775 var ( 776 _ = fmt.Print 777 _ = zip.Store 778 _ p.P 779 _ = regexp.Compile 780 ) 781 `, 782 }, 783 784 // Unused named import is mistaken for unnamed import 785 // golang.org/issue/8149 786 { 787 name: "named_import_doesnt_provide_package_name", 788 in: `package main 789 790 import foo "fmt" 791 792 func main() { fmt.Println() } 793 `, 794 out: `package main 795 796 import "fmt" 797 798 func main() { fmt.Println() } 799 `, 800 }, 801 802 // Unused named import is mistaken for unnamed import 803 // golang.org/issue/8149 804 { 805 name: "unused_named_import_removed", 806 in: `package main 807 808 import ( 809 "fmt" 810 x "fmt" 811 ) 812 813 func main() { fmt.Println() } 814 `, 815 out: `package main 816 817 import ( 818 "fmt" 819 ) 820 821 func main() { fmt.Println() } 822 `, 823 }, 824 825 { 826 name: "ignore_unexported_identifier", 827 in: `package main 828 var _ = fmt.unexported`, 829 out: `package main 830 831 var _ = fmt.unexported 832 `, 833 }, 834 835 // FormatOnly 836 { 837 name: "formatonly_works", 838 formatOnly: true, 839 in: `package main 840 841 import ( 842 "fmt" 843 "manypackages.com/packagea" 844 ) 845 846 func main() {} 847 `, 848 out: `package main 849 850 import ( 851 "fmt" 852 853 "manypackages.com/packagea" 854 ) 855 856 func main() {} 857 `, 858 }, 859 860 { 861 name: "preserve_import_group", 862 in: `package p 863 864 import ( 865 "bytes" 866 "fmt" 867 ) 868 869 var _ = fmt.Sprintf 870 `, 871 out: `package p 872 873 import ( 874 "fmt" 875 ) 876 877 var _ = fmt.Sprintf 878 `, 879 }, 880 { 881 name: "import_grouping_not_path_dependent_no_groups", 882 in: `package main 883 884 import ( 885 "time" 886 ) 887 888 func main() { 889 _ = snappy.ErrCorrupt 890 _ = p.P 891 _ = time.Parse 892 } 893 `, 894 out: `package main 895 896 import ( 897 "time" 898 899 "github.com/golang/snappy" 900 "rsc.io/p" 901 ) 902 903 func main() { 904 _ = snappy.ErrCorrupt 905 _ = p.P 906 _ = time.Parse 907 } 908 `, 909 }, 910 911 { 912 name: "import_grouping_not_path_dependent_existing_group", 913 in: `package main 914 915 import ( 916 "time" 917 918 "github.com/golang/snappy" 919 ) 920 921 func main() { 922 _ = snappy.ErrCorrupt 923 _ = p.P 924 _ = time.Parse 925 } 926 `, 927 out: `package main 928 929 import ( 930 "time" 931 932 "github.com/golang/snappy" 933 "rsc.io/p" 934 ) 935 936 func main() { 937 _ = snappy.ErrCorrupt 938 _ = p.P 939 _ = time.Parse 940 } 941 `, 942 }, 943 944 // golang.org/issue/12097 945 { 946 name: "package_statement_insertion_preserves_comments", 947 in: `// a 948 // b 949 // c 950 951 func main() { 952 _ = fmt.Println 953 }`, 954 out: `package main 955 956 import "fmt" 957 958 // a 959 // b 960 // c 961 962 func main() { 963 _ = fmt.Println 964 } 965 `, 966 }, 967 968 { 969 name: "import_comment_stays_on_import", 970 in: `package main 971 972 import ( 973 "math" // fun 974 ) 975 976 func main() { 977 x := math.MaxInt64 978 fmt.Println(strings.Join(",", []string{"hi"}), x) 979 }`, 980 out: `package main 981 982 import ( 983 "fmt" 984 "math" // fun 985 "strings" 986 ) 987 988 func main() { 989 x := math.MaxInt64 990 fmt.Println(strings.Join(",", []string{"hi"}), x) 991 } 992 `, 993 }, 994 995 { 996 name: "no_blank_after_comment", 997 in: `package main 998 999 import ( 1000 _ "io" 1001 _ "net/http" 1002 _ "net/http/pprof" // install the pprof http handlers 1003 _ "strings" 1004 ) 1005 1006 func main() { 1007 } 1008 `, 1009 out: `package main 1010 1011 import ( 1012 _ "io" 1013 _ "net/http" 1014 _ "net/http/pprof" // install the pprof http handlers 1015 _ "strings" 1016 ) 1017 1018 func main() { 1019 } 1020 `, 1021 }, 1022 1023 { 1024 name: "no_blank_after_comment_reordered", 1025 in: `package main 1026 1027 import ( 1028 _ "io" 1029 _ "net/http/pprof" // install the pprof http handlers 1030 _ "net/http" 1031 _ "strings" 1032 ) 1033 1034 func main() { 1035 } 1036 `, 1037 out: `package main 1038 1039 import ( 1040 _ "io" 1041 _ "net/http" 1042 _ "net/http/pprof" // install the pprof http handlers 1043 _ "strings" 1044 ) 1045 1046 func main() { 1047 } 1048 `, 1049 }, 1050 1051 { 1052 name: "no_blank_after_comment_unnamed", 1053 in: `package main 1054 1055 import ( 1056 "encoding/json" 1057 "io" 1058 "net/http" 1059 _ "net/http/pprof" // install the pprof http handlers 1060 "strings" 1061 1062 "manypackages.com/packagea" 1063 ) 1064 1065 func main() { 1066 _ = strings.ToUpper("hello") 1067 _ = io.EOF 1068 var ( 1069 _ json.Number 1070 _ *http.Request 1071 _ packagea.A 1072 ) 1073 } 1074 `, 1075 out: `package main 1076 1077 import ( 1078 "encoding/json" 1079 "io" 1080 "net/http" 1081 _ "net/http/pprof" // install the pprof http handlers 1082 "strings" 1083 1084 "manypackages.com/packagea" 1085 ) 1086 1087 func main() { 1088 _ = strings.ToUpper("hello") 1089 _ = io.EOF 1090 var ( 1091 _ json.Number 1092 _ *http.Request 1093 _ packagea.A 1094 ) 1095 } 1096 `, 1097 }, 1098 1099 { 1100 name: "blank_after_package_statement_with_comment", 1101 in: `package p // comment 1102 1103 import "math" 1104 1105 var _ = fmt.Printf 1106 `, 1107 out: `package p // comment 1108 1109 import "fmt" 1110 1111 var _ = fmt.Printf 1112 `, 1113 }, 1114 1115 { 1116 name: "blank_after_package_statement_no_comment", 1117 in: `package p 1118 1119 import "math" 1120 1121 var _ = fmt.Printf 1122 `, 1123 out: `package p 1124 1125 import "fmt" 1126 1127 var _ = fmt.Printf 1128 `, 1129 }, 1130 1131 { 1132 name: "cryptorand_preferred_easy_possible", 1133 in: `package p 1134 1135 var _ = rand.Read 1136 `, 1137 out: `package p 1138 1139 import "crypto/rand" 1140 1141 var _ = rand.Read 1142 `, 1143 }, 1144 1145 { 1146 name: "cryptorand_preferred_easy_impossible", 1147 in: `package p 1148 1149 var _ = rand.NewZipf 1150 `, 1151 out: `package p 1152 1153 import "math/rand" 1154 1155 var _ = rand.NewZipf 1156 `, 1157 }, 1158 1159 { 1160 name: "cryptorand_preferred_complex_possible", 1161 in: `package p 1162 1163 var _, _ = rand.Read, rand.Prime 1164 `, 1165 out: `package p 1166 1167 import "crypto/rand" 1168 1169 var _, _ = rand.Read, rand.Prime 1170 `, 1171 }, 1172 1173 { 1174 name: "cryptorand_preferred_complex_impossible", 1175 in: `package p 1176 1177 var _, _ = rand.Read, rand.NewZipf 1178 `, 1179 out: `package p 1180 1181 import "math/rand" 1182 1183 var _, _ = rand.Read, rand.NewZipf 1184 `, 1185 }, 1186 } 1187 1188 func TestSimpleCases(t *testing.T) { 1189 const localPrefix = "local.com,github.com/local" 1190 for _, tt := range tests { 1191 t.Run(tt.name, func(t *testing.T) { 1192 testConfig{ 1193 modules: []packagestest.Module{ 1194 { 1195 Name: "golang.org/fake", 1196 Files: fm{"x.go": tt.in}, 1197 }, 1198 // Skeleton non-stdlib packages for use during testing. 1199 // Each includes one arbitrary symbol, e.g. the first declaration in the first file. 1200 // Try not to add more without a good reason. 1201 // DO NOT USE PACKAGES NOT LISTED HERE -- they will be downloaded! 1202 { 1203 Name: "rsc.io", 1204 Files: fm{"p/x.go": "package p\nfunc P(){}\n"}, 1205 }, 1206 { 1207 Name: "github.com/golang/snappy", 1208 Files: fm{"x.go": "package snappy\nvar ErrCorrupt error\n"}, 1209 }, 1210 { 1211 Name: "manypackages.com", 1212 Files: fm{ 1213 "packagea/x.go": "package packagea\nfunc A(){}\n", 1214 "packageb/x.go": "package packageb\nfunc B(){}\n", 1215 "packagec/x.go": "package packagec\nfunc C(){}\n", 1216 "packaged/x.go": "package packaged\nfunc D(){}\n", 1217 }, 1218 }, 1219 { 1220 Name: "local.com", 1221 Files: fm{"foo/x.go": "package foo\nfunc Foo(){}\n"}, 1222 }, 1223 { 1224 Name: "github.com/local", 1225 Files: fm{"bar/x.go": "package bar\nfunc Bar(){}\n"}, 1226 }, 1227 }, 1228 }.test(t, func(t *goimportTest) { 1229 options := &Options{ 1230 LocalPrefix: localPrefix, 1231 TabWidth: 8, 1232 TabIndent: true, 1233 Comments: true, 1234 Fragment: true, 1235 FormatOnly: tt.formatOnly, 1236 } 1237 t.assertProcessEquals("golang.org/fake", "x.go", nil, options, tt.out) 1238 }) 1239 1240 }) 1241 } 1242 } 1243 1244 func TestAppengine(t *testing.T) { 1245 const input = `package p 1246 1247 var _, _, _ = fmt.Printf, appengine.Main, datastore.ErrInvalidEntityType 1248 ` 1249 1250 const want = `package p 1251 1252 import ( 1253 "fmt" 1254 1255 "appengine" 1256 "appengine/datastore" 1257 ) 1258 1259 var _, _, _ = fmt.Printf, appengine.Main, datastore.ErrInvalidEntityType 1260 ` 1261 1262 testConfig{ 1263 gopathOnly: true, // can't create a module named appengine, so no module tests. 1264 modules: []packagestest.Module{ 1265 { 1266 Name: "golang.org/fake", 1267 Files: fm{"x.go": input}, 1268 }, 1269 { 1270 Name: "appengine", 1271 Files: fm{ 1272 "x.go": "package appengine\nfunc Main(){}\n", 1273 "datastore/x.go": "package datastore\nvar ErrInvalidEntityType error\n", 1274 }, 1275 }, 1276 }, 1277 }.processTest(t, "golang.org/fake", "x.go", nil, nil, want) 1278 } 1279 1280 func TestReadFromFilesystem(t *testing.T) { 1281 tests := []struct { 1282 name string 1283 in, out string 1284 }{ 1285 { 1286 name: "works", 1287 in: `package foo 1288 func bar() { 1289 fmt.Println("hi") 1290 } 1291 `, 1292 out: `package foo 1293 1294 import "fmt" 1295 1296 func bar() { 1297 fmt.Println("hi") 1298 } 1299 `, 1300 }, 1301 { 1302 name: "missing_package", 1303 in: ` 1304 func bar() { 1305 fmt.Println("hi") 1306 } 1307 `, 1308 out: ` 1309 import "fmt" 1310 1311 func bar() { 1312 fmt.Println("hi") 1313 } 1314 `, 1315 }, 1316 } 1317 1318 for _, tt := range tests { 1319 t.Run(tt.name, func(t *testing.T) { 1320 options := &Options{ 1321 TabWidth: 8, 1322 TabIndent: true, 1323 Comments: true, 1324 Fragment: true, 1325 } 1326 testConfig{ 1327 module: packagestest.Module{ 1328 Name: "golang.org/fake", 1329 Files: fm{"x.go": tt.in}, 1330 }, 1331 }.processTest(t, "golang.org/fake", "x.go", nil, options, tt.out) 1332 }) 1333 } 1334 1335 } 1336 1337 // Test support for packages in GOPATH that are actually symlinks. 1338 // Also test that a symlink loop does not block the process. 1339 func TestImportSymlinks(t *testing.T) { 1340 const input = `package p 1341 1342 var ( 1343 _ = fmt.Print 1344 _ = mypkg.Foo 1345 ) 1346 ` 1347 const want = `package p 1348 1349 import ( 1350 "fmt" 1351 1352 "golang.org/fake/x/y/mypkg" 1353 ) 1354 1355 var ( 1356 _ = fmt.Print 1357 _ = mypkg.Foo 1358 ) 1359 ` 1360 1361 testConfig{ 1362 module: packagestest.Module{ 1363 Name: "golang.org/fake", 1364 Files: fm{ 1365 "target/f.go": "package mypkg\nvar Foo = 123\n", 1366 "x/y/mypkg": packagestest.Symlink("../../target"), // valid symlink 1367 "x/y/apkg": packagestest.Symlink(".."), // symlink loop 1368 "myotherpackage/toformat.go": input, 1369 }, 1370 }, 1371 }.processTest(t, "golang.org/fake", "myotherpackage/toformat.go", nil, nil, want) 1372 } 1373 1374 func TestImportSymlinksWithIgnore(t *testing.T) { 1375 const input = `package p 1376 1377 var ( 1378 _ = fmt.Print 1379 _ = mypkg.Foo 1380 ) 1381 ` 1382 const want = `package p 1383 1384 import "fmt" 1385 1386 var ( 1387 _ = fmt.Print 1388 _ = mypkg.Foo 1389 ) 1390 ` 1391 1392 testConfig{ 1393 gopathOnly: true, 1394 module: packagestest.Module{ 1395 Name: "golang.org/fake", 1396 Files: fm{ 1397 "target/f.go": "package mypkg\nvar Foo = 123\n", 1398 "x/y/mypkg": packagestest.Symlink("../../target"), // valid symlink 1399 "x/y/apkg": packagestest.Symlink(".."), // symlink loop 1400 "myotherpkg/toformat.go": input, 1401 "../../.goimportsignore": "golang.org/fake/x/y/mypkg\n", 1402 }, 1403 }, 1404 }.processTest(t, "golang.org/fake", "myotherpkg/toformat.go", nil, nil, want) 1405 } 1406 1407 // Test for x/y/v2 convention for package y. 1408 func TestModuleVersion(t *testing.T) { 1409 const input = `package p 1410 1411 import ( 1412 "fmt" 1413 1414 "github.com/foo/v2" 1415 ) 1416 1417 var ( 1418 _ = fmt.Print 1419 _ = foo.Foo 1420 ) 1421 ` 1422 1423 testConfig{ 1424 modules: []packagestest.Module{ 1425 { 1426 Name: "mypkg.com/outpkg", 1427 Files: fm{"toformat.go": input}, 1428 }, 1429 { 1430 Name: "github.com/foo/v2", 1431 Files: fm{"x.go": "package foo\n func Foo(){}\n"}, 1432 }, 1433 }, 1434 }.processTest(t, "mypkg.com/outpkg", "toformat.go", nil, nil, input) 1435 } 1436 1437 // Test for correctly identifying the name of a vendored package when it 1438 // differs from its directory name. In this test, the import line 1439 // "mypkg.com/mypkg_v1" would be removed if goimports wasn't able to detect 1440 // that the package name is "mypkg". 1441 func TestVendorPackage(t *testing.T) { 1442 const input = `package p 1443 import ( 1444 "fmt" 1445 "mypkg.com/mypkg_v1" 1446 ) 1447 var _, _ = fmt.Print, mypkg.Foo 1448 ` 1449 1450 const want = `package p 1451 1452 import ( 1453 "fmt" 1454 1455 mypkg "mypkg.com/mypkg_v1" 1456 ) 1457 1458 var _, _ = fmt.Print, mypkg.Foo 1459 ` 1460 1461 testConfig{ 1462 gopathOnly: true, 1463 module: packagestest.Module{ 1464 Name: "mypkg.com/outpkg", 1465 Files: fm{ 1466 "vendor/mypkg.com/mypkg_v1/f.go": "package mypkg\nvar Foo = 123\n", 1467 "toformat.go": input, 1468 }, 1469 }, 1470 }.processTest(t, "mypkg.com/outpkg", "toformat.go", nil, nil, want) 1471 } 1472 1473 func TestInternal(t *testing.T) { 1474 const input = `package bar 1475 1476 var _ = race.Acquire 1477 ` 1478 const importAdded = `package bar 1479 1480 import "foo.com/internal/race" 1481 1482 var _ = race.Acquire 1483 ` 1484 1485 // Packages under the same directory should be able to use internal packages. 1486 testConfig{ 1487 module: packagestest.Module{ 1488 Name: "foo.com", 1489 Files: fm{ 1490 "internal/race/x.go": "package race\n func Acquire(){}\n", 1491 "bar/x.go": input, 1492 }, 1493 }, 1494 }.processTest(t, "foo.com", "bar/x.go", nil, nil, importAdded) 1495 1496 // Packages outside the same directory should not. 1497 testConfig{ 1498 modules: []packagestest.Module{ 1499 { 1500 Name: "foo.com", 1501 Files: fm{"internal/race/x.go": "package race\n func Acquire(){}\n"}, 1502 }, 1503 { 1504 Name: "bar.com", 1505 Files: fm{"x.go": input}, 1506 }, 1507 }, 1508 }.processTest(t, "bar.com", "x.go", nil, nil, input) 1509 } 1510 1511 func TestProcessVendor(t *testing.T) { 1512 const input = `package p 1513 1514 var _ = hpack.HuffmanDecode 1515 ` 1516 const want = `package p 1517 1518 import "golang.org/x/net/http2/hpack" 1519 1520 var _ = hpack.HuffmanDecode 1521 ` 1522 testConfig{ 1523 gopathOnly: true, 1524 module: packagestest.Module{ 1525 Name: "foo.com", 1526 Files: fm{ 1527 "vendor/golang.org/x/net/http2/hpack/huffman.go": "package hpack\nfunc HuffmanDecode() { }\n", 1528 "bar/x.go": input, 1529 }, 1530 }, 1531 }.processTest(t, "foo.com", "bar/x.go", nil, nil, want) 1532 } 1533 1534 func TestFindStdlib(t *testing.T) { 1535 tests := []struct { 1536 pkg string 1537 symbols []string 1538 want string 1539 }{ 1540 {"http", []string{"Get"}, "net/http"}, 1541 {"http", []string{"Get", "Post"}, "net/http"}, 1542 {"http", []string{"Get", "Foo"}, ""}, 1543 {"bytes", []string{"Buffer"}, "bytes"}, 1544 {"ioutil", []string{"Discard"}, "io/ioutil"}, 1545 } 1546 for _, tt := range tests { 1547 input := "package p\n" 1548 for _, sym := range tt.symbols { 1549 input += fmt.Sprintf("var _ = %s.%s\n", tt.pkg, sym) 1550 } 1551 testConfig{ 1552 module: packagestest.Module{ 1553 Name: "foo.com", 1554 Files: fm{"x.go": input}, 1555 }, 1556 }.test(t, func(t *goimportTest) { 1557 buf, err := t.process("foo.com", "x.go", nil, nil) 1558 if err != nil { 1559 t.Fatal(err) 1560 } 1561 if got := string(buf); !strings.Contains(got, tt.want) { 1562 t.Errorf("Process(%q) = %q, wanted it to contain %q", input, buf, tt.want) 1563 } 1564 }) 1565 } 1566 } 1567 1568 // https://golang.org/issue/31814 1569 func TestStdlibNotPrefixed(t *testing.T) { 1570 const input = `package p 1571 var _ = bytes.Buffer 1572 ` 1573 const want = `package p 1574 1575 import "bytes" 1576 1577 var _ = bytes.Buffer 1578 ` 1579 // Force a scan of the stdlib. 1580 savedStdlib := stdlib 1581 defer func() { stdlib = savedStdlib }() 1582 stdlib = map[string][]string{} 1583 1584 testConfig{ 1585 module: packagestest.Module{ 1586 Name: "ignored.com", 1587 Files: fm{"x.go": "package x"}, 1588 }, 1589 }.test(t, func(t *goimportTest) { 1590 // Run in GOROOT/src so that the std module shows up in go list -m all. 1591 t.env.WorkingDir = filepath.Join(t.goroot, "src") 1592 got, err := t.processNonModule(filepath.Join(t.goroot, "src/x.go"), []byte(input), nil) 1593 if err != nil { 1594 t.Fatalf("Process() = %v", err) 1595 } 1596 if string(got) != want { 1597 t.Errorf("Got:\n%s\nWant:\n%s", got, want) 1598 } 1599 }) 1600 } 1601 1602 func TestStdlibSelfImports(t *testing.T) { 1603 const input = `package ecdsa 1604 1605 var _ = ecdsa.GenerateKey 1606 ` 1607 1608 testConfig{ 1609 module: packagestest.Module{ 1610 Name: "ignored.com", 1611 Files: fm{"x.go": "package x"}, 1612 }, 1613 }.test(t, func(t *goimportTest) { 1614 got, err := t.processNonModule(filepath.Join(t.goroot, "src/crypto/ecdsa/foo.go"), []byte(input), nil) 1615 if err != nil { 1616 t.Fatalf("Process() = %v", err) 1617 } 1618 if string(got) != input { 1619 t.Errorf("Got:\n%s\nWant:\n%s", got, input) 1620 } 1621 }) 1622 } 1623 1624 type testConfig struct { 1625 gopathOnly bool 1626 module packagestest.Module 1627 modules []packagestest.Module 1628 } 1629 1630 // fm is the type for a packagestest.Module's Files, abbreviated for shorter lines. 1631 type fm map[string]interface{} 1632 1633 func (c testConfig) test(t *testing.T, fn func(*goimportTest)) { 1634 t.Helper() 1635 1636 if c.module.Name != "" { 1637 c.modules = []packagestest.Module{c.module} 1638 } 1639 1640 for _, exporter := range packagestest.All { 1641 t.Run(exporter.Name(), func(t *testing.T) { 1642 t.Helper() 1643 if c.gopathOnly && exporter.Name() == "Modules" { 1644 t.Skip("test marked GOPATH-only") 1645 } 1646 exported := packagestest.Export(t, exporter, c.modules) 1647 defer exported.Cleanup() 1648 1649 env := map[string]string{} 1650 for _, kv := range exported.Config.Env { 1651 split := strings.SplitN(kv, "=", 2) 1652 env[split[0]] = split[1] 1653 } 1654 it := &goimportTest{ 1655 T: t, 1656 env: &ProcessEnv{ 1657 Env: env, 1658 WorkingDir: exported.Config.Dir, 1659 GocmdRunner: &gocommand.Runner{}, 1660 }, 1661 exported: exported, 1662 } 1663 if *testDebug { 1664 it.env.Logf = log.Printf 1665 } 1666 // packagestest clears out GOROOT to work around golang/go#32849, 1667 // which isn't relevant here. Fill it back in so we can find the standard library. 1668 it.env.Env["GOROOT"] = build.Default.GOROOT 1669 it.goroot = build.Default.GOROOT 1670 1671 fn(it) 1672 }) 1673 } 1674 } 1675 1676 func (c testConfig) processTest(t *testing.T, module, file string, contents []byte, opts *Options, want string) { 1677 t.Helper() 1678 c.test(t, func(t *goimportTest) { 1679 t.Helper() 1680 t.assertProcessEquals(module, file, contents, opts, want) 1681 }) 1682 } 1683 1684 type goimportTest struct { 1685 *testing.T 1686 goroot string 1687 env *ProcessEnv 1688 exported *packagestest.Exported 1689 } 1690 1691 func (t *goimportTest) process(module, file string, contents []byte, opts *Options) ([]byte, error) { 1692 t.Helper() 1693 f := t.exported.File(module, file) 1694 if f == "" { 1695 t.Fatalf("%v not found in exported files (typo in filename?)", file) 1696 } 1697 return t.processNonModule(f, contents, opts) 1698 } 1699 1700 func (t *goimportTest) processNonModule(file string, contents []byte, opts *Options) ([]byte, error) { 1701 if contents == nil { 1702 var err error 1703 contents, err = ioutil.ReadFile(file) 1704 if err != nil { 1705 return nil, err 1706 } 1707 } 1708 if opts == nil { 1709 opts = &Options{Comments: true, TabIndent: true, TabWidth: 8} 1710 } 1711 // ProcessEnv is not safe for concurrent use. Make a copy. 1712 opts.Env = t.env.CopyConfig() 1713 return Process(file, contents, opts) 1714 } 1715 1716 func (t *goimportTest) assertProcessEquals(module, file string, contents []byte, opts *Options, want string) { 1717 buf, err := t.process(module, file, contents, opts) 1718 if err != nil { 1719 t.Fatalf("Process() = %v", err) 1720 } 1721 if string(buf) != want { 1722 t.Errorf("Got:\n'%s'\nWant:\n'%s'", buf, want) // 's show empty lines 1723 } 1724 } 1725 1726 // Tests that added imports are renamed when the import path's base doesn't 1727 // match its package name. 1728 func TestRenameWhenPackageNameMismatch(t *testing.T) { 1729 const input = `package main 1730 const Y = bar.X` 1731 1732 const want = `package main 1733 1734 import bar "foo.com/foo/bar/baz" 1735 1736 const Y = bar.X 1737 ` 1738 testConfig{ 1739 module: packagestest.Module{ 1740 Name: "foo.com", 1741 Files: fm{ 1742 "foo/bar/baz/x.go": "package bar \n const X = 1", 1743 "test/t.go": input, 1744 }, 1745 }, 1746 }.processTest(t, "foo.com", "test/t.go", nil, nil, want) 1747 } 1748 1749 func TestPanicAstutils(t *testing.T) { 1750 t.Skip("panic in ast/astutil/imports.go, should be PostionFor(,false) at lines 273, 274, at least") 1751 const input = `package main 1752 //line mah.go:600 1753 1754 import ( 1755 "foo.com/a.thing" 1756 "foo.com/surprise" 1757 "foo.com/v1" 1758 "foo.com/other/v2" 1759 "foo.com/other/v3" 1760 ) 1761 ` 1762 1763 const want = `package main 1764 1765 //line mah.go:600 1766 1767 import ( 1768 "foo.com/a.thing" 1769 "foo.com/go-thing" 1770 gow "foo.com/go-wrong" 1771 v2 "foo.com/other/v2" 1772 "foo.com/other/v3" 1773 bar "foo.com/surprise" 1774 v1 "foo.com/v1" 1775 ) 1776 1777 ` 1778 1779 testConfig{ 1780 module: packagestest.Module{ 1781 Name: "foo.com", 1782 Files: fm{ 1783 "test/t.go": input, 1784 }, 1785 }, 1786 }.processTest(t, "foo.com", "test/t.go", nil, nil, want) 1787 } 1788 1789 // without PositionFor in sortImports this test panics 1790 func TestPanic51916(t *testing.T) { 1791 const input = `package main 1792 //line mah.go:600 1793 1794 import ( 1795 "foo.com/a.thing" 1796 "foo.com/surprise" 1797 "foo.com/v1" 1798 "foo.com/other/v2" 1799 "foo.com/other/v3" 1800 "foo.com/go-thing" 1801 "foo.com/go-wrong" 1802 ) 1803 1804 var _ = []interface{}{bar.X, v1.Y, a.A, v2.V2, other.V3, thing.Thing, gow.Wrong}` 1805 1806 const want = `package main 1807 1808 //line mah.go:600 1809 1810 import ( 1811 "foo.com/a.thing" 1812 "foo.com/go-thing" 1813 gow "foo.com/go-wrong" 1814 v2 "foo.com/other/v2" 1815 "foo.com/other/v3" 1816 bar "foo.com/surprise" 1817 v1 "foo.com/v1" 1818 ) 1819 1820 var _ = []interface{}{bar.X, v1.Y, a.A, v2.V2, other.V3, thing.Thing, gow.Wrong} 1821 ` 1822 1823 testConfig{ 1824 module: packagestest.Module{ 1825 Name: "foo.com", 1826 Files: fm{ 1827 "a.thing/a.go": "package a \n const A = 1", 1828 "surprise/x.go": "package bar \n const X = 1", 1829 "v1/x.go": "package v1 \n const Y = 1", 1830 "other/v2/y.go": "package v2 \n const V2 = 1", 1831 "other/v3/z.go": "package other \n const V3 = 1", 1832 "go-thing/b.go": "package thing \n const Thing = 1", 1833 "go-wrong/b.go": "package gow \n const Wrong = 1", 1834 "test/t.go": input, 1835 }, 1836 }, 1837 }.processTest(t, "foo.com", "test/t.go", nil, nil, want) 1838 } 1839 1840 // Tests that an existing import with badly mismatched path/name has its name 1841 // correctly added. See #28645 and #29041. 1842 // and check that //line directives are ignored (#51916) 1843 func TestAddNameToMismatchedImport(t *testing.T) { 1844 const input = `package main 1845 1846 import ( 1847 "foo.com/a.thing" 1848 "foo.com/surprise" 1849 "foo.com/v1" 1850 "foo.com/other/v2" 1851 "foo.com/other/v3" 1852 "foo.com/go-thing" 1853 "foo.com/go-wrong" 1854 ) 1855 1856 var _ = []interface{}{bar.X, v1.Y, a.A, v2.V2, other.V3, thing.Thing, gow.Wrong}` 1857 1858 const want = `package main 1859 1860 import ( 1861 "foo.com/a.thing" 1862 "foo.com/go-thing" 1863 gow "foo.com/go-wrong" 1864 v2 "foo.com/other/v2" 1865 "foo.com/other/v3" 1866 bar "foo.com/surprise" 1867 v1 "foo.com/v1" 1868 ) 1869 1870 var _ = []interface{}{bar.X, v1.Y, a.A, v2.V2, other.V3, thing.Thing, gow.Wrong} 1871 ` 1872 1873 testConfig{ 1874 module: packagestest.Module{ 1875 Name: "foo.com", 1876 Files: fm{ 1877 "a.thing/a.go": "package a \n const A = 1", 1878 "surprise/x.go": "package bar \n const X = 1", 1879 "v1/x.go": "package v1 \n const Y = 1", 1880 "other/v2/y.go": "package v2 \n const V2 = 1", 1881 "other/v3/z.go": "package other \n const V3 = 1", 1882 "go-thing/b.go": "package thing \n const Thing = 1", 1883 "go-wrong/b.go": "package gow \n const Wrong = 1", 1884 "test/t.go": input, 1885 }, 1886 }, 1887 }.processTest(t, "foo.com", "test/t.go", nil, nil, want) 1888 } 1889 1890 // Tests that the LocalPrefix option causes imports 1891 // to be added into a later group (num=3). 1892 func TestLocalPrefix(t *testing.T) { 1893 tests := []struct { 1894 name string 1895 modules []packagestest.Module 1896 localPrefix string 1897 src string 1898 want string 1899 }{ 1900 { 1901 name: "one_local", 1902 modules: []packagestest.Module{ 1903 { 1904 Name: "foo.com", 1905 Files: fm{ 1906 "bar/bar.go": "package bar \n const X = 1", 1907 }, 1908 }, 1909 }, 1910 localPrefix: "foo.com/", 1911 src: "package main \n const Y = bar.X \n const _ = runtime.GOOS", 1912 want: `package main 1913 1914 import ( 1915 "runtime" 1916 1917 "foo.com/bar" 1918 ) 1919 1920 const Y = bar.X 1921 const _ = runtime.GOOS 1922 `, 1923 }, 1924 { 1925 name: "two_local", 1926 modules: []packagestest.Module{ 1927 { 1928 Name: "foo.com", 1929 Files: fm{ 1930 "foo/foo.go": "package foo \n const X = 1", 1931 "foo/bar/bar.go": "package bar \n const X = 1", 1932 }, 1933 }, 1934 }, 1935 localPrefix: "foo.com/foo", 1936 src: "package main \n const Y = bar.X \n const Z = foo.X \n const _ = runtime.GOOS", 1937 want: `package main 1938 1939 import ( 1940 "runtime" 1941 1942 "foo.com/foo" 1943 "foo.com/foo/bar" 1944 ) 1945 1946 const Y = bar.X 1947 const Z = foo.X 1948 const _ = runtime.GOOS 1949 `, 1950 }, 1951 { 1952 name: "three_prefixes", 1953 modules: []packagestest.Module{ 1954 { 1955 Name: "example.org/pkg", 1956 Files: fm{"pkg.go": "package pkg \n const A = 1"}, 1957 }, 1958 { 1959 Name: "foo.com", 1960 Files: fm{"bar/bar.go": "package bar \n const B = 1"}, 1961 }, 1962 { 1963 Name: "code.org/r/p", 1964 Files: fm{"expproj/expproj.go": "package expproj \n const C = 1"}, 1965 }, 1966 }, 1967 localPrefix: "example.org/pkg,foo.com/,code.org", 1968 src: "package main \n const X = pkg.A \n const Y = bar.B \n const Z = expproj.C \n const _ = runtime.GOOS", 1969 want: `package main 1970 1971 import ( 1972 "runtime" 1973 1974 "code.org/r/p/expproj" 1975 "example.org/pkg" 1976 "foo.com/bar" 1977 ) 1978 1979 const X = pkg.A 1980 const Y = bar.B 1981 const Z = expproj.C 1982 const _ = runtime.GOOS 1983 `, 1984 }, 1985 } 1986 1987 for _, tt := range tests { 1988 t.Run(tt.name, func(t *testing.T) { 1989 testConfig{ 1990 // The module being processed has to be first so it's the primary module. 1991 modules: append([]packagestest.Module{{ 1992 Name: "test.com", 1993 Files: fm{"t.go": tt.src}, 1994 }}, tt.modules...), 1995 }.test(t, func(t *goimportTest) { 1996 options := &Options{ 1997 LocalPrefix: tt.localPrefix, 1998 TabWidth: 8, 1999 TabIndent: true, 2000 Comments: true, 2001 Fragment: true, 2002 } 2003 t.assertProcessEquals("test.com", "t.go", nil, options, tt.want) 2004 }) 2005 }) 2006 } 2007 } 2008 2009 // Tests that "package documentation" files are ignored. 2010 func TestIgnoreDocumentationPackage(t *testing.T) { 2011 const input = `package x 2012 2013 const Y = foo.X 2014 ` 2015 const want = `package x 2016 2017 import "foo.com/foo" 2018 2019 const Y = foo.X 2020 ` 2021 2022 testConfig{ 2023 module: packagestest.Module{ 2024 Name: "foo.com", 2025 Files: fm{ 2026 "foo/foo.go": "package foo\nconst X = 1\n", 2027 "foo/doc.go": "package documentation \n // just to confuse things\n", 2028 "x/x.go": input, 2029 }, 2030 }, 2031 }.processTest(t, "foo.com", "x/x.go", nil, nil, want) 2032 } 2033 2034 // Tests importPathToNameGoPathParse and in particular that it stops 2035 // after finding the first non-documentation package name, not 2036 // reporting an error on inconsistent package names (since it should 2037 // never make it that far). 2038 func TestImportPathToNameGoPathParse(t *testing.T) { 2039 testConfig{ 2040 module: packagestest.Module{ 2041 Name: "example.net/pkg", 2042 Files: fm{ 2043 "doc.go": "package documentation\n", // ignored 2044 "gen.go": "package main\n", // also ignored 2045 "pkg.go": "package the_pkg_name_to_find\n and this syntax error is ignored because of parser.PackageClauseOnly", 2046 "z.go": "package inconsistent\n", // inconsistent but ignored 2047 }, 2048 }, 2049 }.test(t, func(t *goimportTest) { 2050 if strings.Contains(t.Name(), "GoPackages") { 2051 t.Skip("go/packages does not ignore package main") 2052 } 2053 r, err := t.env.GetResolver() 2054 if err != nil { 2055 t.Fatal(err) 2056 } 2057 srcDir := filepath.Dir(t.exported.File("example.net/pkg", "z.go")) 2058 names, err := r.loadPackageNames([]string{"example.net/pkg"}, srcDir) 2059 if err != nil { 2060 t.Fatal(err) 2061 } 2062 const want = "the_pkg_name_to_find" 2063 if got := names["example.net/pkg"]; got != want { 2064 t.Errorf("loadPackageNames(..) = %q; want %q", got, want) 2065 } 2066 }) 2067 } 2068 2069 func TestIgnoreConfiguration(t *testing.T) { 2070 const input = `package x 2071 2072 const _ = pkg.X 2073 ` 2074 const want = `package x 2075 2076 import "foo.com/otherwise-longer-so-worse-example/foo/pkg" 2077 2078 const _ = pkg.X 2079 ` 2080 2081 testConfig{ 2082 gopathOnly: true, 2083 module: packagestest.Module{ 2084 Name: "foo.com", 2085 Files: fm{ 2086 "../.goimportsignore": "# comment line\n\n foo.com/example", // tests comment, blank line, whitespace trimming 2087 "example/pkg/pkg.go": "package pkg\nconst X = 1", 2088 "otherwise-longer-so-worse-example/foo/pkg/pkg.go": "package pkg\nconst X = 1", 2089 "x/x.go": input, 2090 }, 2091 }, 2092 }.processTest(t, "foo.com", "x/x.go", nil, nil, want) 2093 } 2094 2095 // Skip "node_modules" directory. 2096 func TestSkipNodeModules(t *testing.T) { 2097 const input = `package x 2098 2099 const _ = pkg.X 2100 ` 2101 const want = `package x 2102 2103 import "foo.com/otherwise-longer/not_modules/pkg" 2104 2105 const _ = pkg.X 2106 ` 2107 2108 testConfig{ 2109 gopathOnly: true, 2110 module: packagestest.Module{ 2111 Name: "foo.com", 2112 Files: fm{ 2113 "example/node_modules/pkg/a.go": "package pkg\nconst X = 1", 2114 "otherwise-longer/not_modules/pkg/a.go": "package pkg\nconst X = 1", 2115 "x/x.go": input, 2116 }, 2117 }, 2118 }.processTest(t, "foo.com", "x/x.go", nil, nil, want) 2119 } 2120 2121 // Tests that package global variables with the same name and function name as 2122 // a function in a separate package do not result in an import which masks 2123 // the global variable 2124 func TestGlobalImports(t *testing.T) { 2125 const usesGlobal = `package pkg 2126 2127 func doSomething() { 2128 t := time.Now() 2129 } 2130 ` 2131 2132 const declaresGlobal = `package pkg 2133 2134 type Time struct{} 2135 2136 func (t Time) Now() Time { 2137 return Time{} 2138 } 2139 2140 var time Time 2141 ` 2142 2143 testConfig{ 2144 module: packagestest.Module{ 2145 Name: "foo.com", 2146 Files: fm{ 2147 "pkg/uses.go": usesGlobal, 2148 "pkg/global.go": declaresGlobal, 2149 }, 2150 }, 2151 }.processTest(t, "foo.com", "pkg/uses.go", nil, nil, usesGlobal) 2152 } 2153 2154 // Some people put multiple packages' files in the same directory. Globals 2155 // declared in other packages should be ignored. 2156 func TestGlobalImports_DifferentPackage(t *testing.T) { 2157 const declaresGlobal = `package main 2158 var fmt int 2159 ` 2160 const input = `package pkg 2161 var _ = fmt.Printf 2162 ` 2163 const want = `package pkg 2164 2165 import "fmt" 2166 2167 var _ = fmt.Printf 2168 ` 2169 2170 testConfig{ 2171 module: packagestest.Module{ 2172 Name: "foo.com", 2173 Files: fm{ 2174 "pkg/main.go": declaresGlobal, 2175 "pkg/uses.go": input, 2176 }, 2177 }, 2178 }.processTest(t, "foo.com", "pkg/uses.go", nil, nil, want) 2179 } 2180 2181 func TestGlobalImports_MultipleMains(t *testing.T) { 2182 const declaresGlobal = `package main 2183 var fmt int 2184 ` 2185 const input = `package main 2186 import "fmt" 2187 var _, _ = fmt.Printf, bytes.Equal 2188 ` 2189 const want = `package main 2190 2191 import ( 2192 "bytes" 2193 "fmt" 2194 ) 2195 2196 var _, _ = fmt.Printf, bytes.Equal 2197 ` 2198 2199 testConfig{ 2200 module: packagestest.Module{ 2201 Name: "foo.com", 2202 Files: fm{ 2203 "pkg/main.go": declaresGlobal, 2204 "pkg/uses.go": input, 2205 }, 2206 }, 2207 }.processTest(t, "foo.com", "pkg/uses.go", nil, nil, want) 2208 } 2209 2210 // Tests that sibling files - other files in the same package - can provide an 2211 // import that may not be the default one otherwise. 2212 func TestSiblingImports(t *testing.T) { 2213 2214 // provide is the sibling file that provides the desired import. 2215 const provide = `package siblingimporttest 2216 2217 import "local/log" 2218 import "my/bytes" 2219 import renamed "fmt" 2220 2221 func LogSomething() { 2222 log.Print("Something") 2223 bytes.SomeFunc() 2224 renamed.Println("Something") 2225 } 2226 ` 2227 2228 // need is the file being tested that needs the import. 2229 const need = `package siblingimporttest 2230 2231 var _ = bytes.Buffer{} 2232 2233 func LogSomethingElse() { 2234 log.Print("Something else") 2235 renamed.Println("Yet another") 2236 } 2237 ` 2238 2239 // want is the expected result file 2240 const want = `package siblingimporttest 2241 2242 import ( 2243 "bytes" 2244 renamed "fmt" 2245 "local/log" 2246 ) 2247 2248 var _ = bytes.Buffer{} 2249 2250 func LogSomethingElse() { 2251 log.Print("Something else") 2252 renamed.Println("Yet another") 2253 } 2254 ` 2255 2256 testConfig{ 2257 module: packagestest.Module{ 2258 Name: "foo.com", 2259 Files: fm{ 2260 "p/needs_import.go": need, 2261 "p/provides_import.go": provide, 2262 }, 2263 }, 2264 }.processTest(t, "foo.com", "p/needs_import.go", nil, nil, want) 2265 } 2266 2267 // Tests #29180: a sibling import of the right package with the wrong name is used. 2268 func TestSiblingImport_Misnamed(t *testing.T) { 2269 const sibling = `package main 2270 import renamed "fmt" 2271 var _ = renamed.Printf 2272 ` 2273 const input = `package pkg 2274 var _ = fmt.Printf 2275 ` 2276 const want = `package pkg 2277 2278 import "fmt" 2279 2280 var _ = fmt.Printf 2281 ` 2282 2283 testConfig{ 2284 module: packagestest.Module{ 2285 Name: "foo.com", 2286 Files: fm{ 2287 "pkg/main.go": sibling, 2288 "pkg/uses.go": input, 2289 }, 2290 }, 2291 }.processTest(t, "foo.com", "pkg/uses.go", nil, nil, want) 2292 2293 } 2294 2295 // Tests that an input file's own package is ignored. 2296 func TestIgnoreOwnPackage(t *testing.T) { 2297 const input = `package pkg 2298 2299 const _ = pkg.X 2300 ` 2301 const want = `package pkg 2302 2303 const _ = pkg.X 2304 ` 2305 2306 testConfig{ 2307 module: packagestest.Module{ 2308 Name: "foo.com", 2309 Files: fm{ 2310 "pkg/a.go": "package pkg\nconst X = 1", 2311 "pkg/b.go": input, 2312 }, 2313 }, 2314 }.processTest(t, "foo.com", "pkg/b.go", nil, nil, want) 2315 } 2316 2317 func TestExternalTestImportsPackageUnderTest(t *testing.T) { 2318 const provide = `package pkg 2319 func DoIt(){} 2320 ` 2321 const input = `package pkg_test 2322 2323 var _ = pkg.DoIt` 2324 2325 const want = `package pkg_test 2326 2327 import "foo.com/pkg" 2328 2329 var _ = pkg.DoIt 2330 ` 2331 2332 testConfig{ 2333 module: packagestest.Module{ 2334 Name: "foo.com", 2335 Files: fm{ 2336 "pkg/provide.go": provide, 2337 "pkg/x_test.go": input, 2338 }, 2339 }, 2340 }.processTest(t, "foo.com", "pkg/x_test.go", nil, nil, want) 2341 } 2342 2343 func TestPkgIsCandidate(t *testing.T) { 2344 tests := []struct { 2345 name string 2346 filename string 2347 pkgIdent string 2348 pkg *pkg 2349 want bool 2350 }{ 2351 { 2352 name: "normal_match", 2353 filename: "/gopath/src/my/pkg/pkg.go", 2354 pkgIdent: "client", 2355 pkg: &pkg{ 2356 dir: "/gopath/src/client", 2357 importPathShort: "client", 2358 }, 2359 want: true, 2360 }, 2361 { 2362 name: "no_match", 2363 filename: "/gopath/src/my/pkg/pkg.go", 2364 pkgIdent: "zzz", 2365 pkg: &pkg{ 2366 dir: "/gopath/src/client", 2367 importPathShort: "client", 2368 }, 2369 want: false, 2370 }, 2371 { 2372 name: "match_too_early", 2373 filename: "/gopath/src/my/pkg/pkg.go", 2374 pkgIdent: "client", 2375 pkg: &pkg{ 2376 dir: "/gopath/src/client/foo/foo/foo", 2377 importPathShort: "client/foo/foo", 2378 }, 2379 want: false, 2380 }, 2381 { 2382 name: "substring_match", 2383 filename: "/gopath/src/my/pkg/pkg.go", 2384 pkgIdent: "client", 2385 pkg: &pkg{ 2386 dir: "/gopath/src/foo/go-client", 2387 importPathShort: "foo/go-client", 2388 }, 2389 want: true, 2390 }, 2391 { 2392 name: "hidden_internal", 2393 filename: "/gopath/src/my/pkg/pkg.go", 2394 pkgIdent: "client", 2395 pkg: &pkg{ 2396 dir: "/gopath/src/foo/internal/client", 2397 importPathShort: "foo/internal/client", 2398 }, 2399 want: false, 2400 }, 2401 { 2402 name: "visible_internal", 2403 filename: "/gopath/src/foo/bar.go", 2404 pkgIdent: "client", 2405 pkg: &pkg{ 2406 dir: "/gopath/src/foo/internal/client", 2407 importPathShort: "foo/internal/client", 2408 }, 2409 want: true, 2410 }, 2411 { 2412 name: "invisible_vendor", 2413 filename: "/gopath/src/foo/bar.go", 2414 pkgIdent: "client", 2415 pkg: &pkg{ 2416 dir: "/gopath/src/other/vendor/client", 2417 importPathShort: "client", 2418 }, 2419 want: false, 2420 }, 2421 { 2422 name: "visible_vendor", 2423 filename: "/gopath/src/foo/bar.go", 2424 pkgIdent: "client", 2425 pkg: &pkg{ 2426 dir: "/gopath/src/foo/vendor/client", 2427 importPathShort: "client", 2428 }, 2429 want: true, 2430 }, 2431 { 2432 name: "match_with_hyphens", 2433 filename: "/gopath/src/foo/bar.go", 2434 pkgIdent: "socketio", 2435 pkg: &pkg{ 2436 dir: "/gopath/src/foo/socket-io", 2437 importPathShort: "foo/socket-io", 2438 }, 2439 want: true, 2440 }, 2441 { 2442 name: "match_with_mixed_case", 2443 filename: "/gopath/src/foo/bar.go", 2444 pkgIdent: "fooprod", 2445 pkg: &pkg{ 2446 dir: "/gopath/src/foo/FooPROD", 2447 importPathShort: "foo/FooPROD", 2448 }, 2449 want: true, 2450 }, 2451 { 2452 name: "matches_with_hyphen_and_caps", 2453 filename: "/gopath/src/foo/bar.go", 2454 pkgIdent: "fooprod", 2455 pkg: &pkg{ 2456 dir: "/gopath/src/foo/Foo-PROD", 2457 importPathShort: "foo/Foo-PROD", 2458 }, 2459 want: true, 2460 }, 2461 } 2462 for i, tt := range tests { 2463 t.Run(tt.name, func(t *testing.T) { 2464 refs := references{tt.pkgIdent: nil} 2465 got := pkgIsCandidate(tt.filename, refs, tt.pkg) 2466 if got != tt.want { 2467 t.Errorf("test %d. pkgIsCandidate(%q, %q, %+v) = %v; want %v", 2468 i, tt.filename, tt.pkgIdent, *tt.pkg, got, tt.want) 2469 } 2470 }) 2471 } 2472 } 2473 2474 // Issue 20941: this used to panic on Windows. 2475 func TestProcessStdin(t *testing.T) { 2476 testConfig{ 2477 module: packagestest.Module{ 2478 Name: "foo.com", 2479 }, 2480 }.test(t, func(t *goimportTest) { 2481 got, err := t.processNonModule("<standard input>", []byte("package main\nfunc main() {\n\tfmt.Println(123)\n}\n"), nil) 2482 if err != nil { 2483 t.Fatal(err) 2484 } 2485 if !strings.Contains(string(got), `"fmt"`) { 2486 t.Errorf("expected fmt import; got: %s", got) 2487 } 2488 }) 2489 } 2490 2491 // Tests LocalPackagePromotion when there is a local package that matches, it 2492 // should be the closest match. 2493 // https://golang.org/issues/17557 2494 func TestLocalPackagePromotion(t *testing.T) { 2495 const input = `package main 2496 var c = &config.SystemConfig{} 2497 ` 2498 const want = `package main 2499 2500 import "mycompany.net/tool/config" 2501 2502 var c = &config.SystemConfig{} 2503 ` 2504 2505 testConfig{ 2506 modules: []packagestest.Module{ 2507 { 2508 Name: "config.net/config", 2509 Files: fm{"config.go": "package config\n type SystemConfig struct {}"}, // Will match but should not be first choice 2510 }, 2511 { 2512 Name: "mycompany.net/config", 2513 Files: fm{"config.go": "package config\n type SystemConfig struct {}"}, // Will match but should not be first choice 2514 }, 2515 { 2516 Name: "mycompany.net/tool", 2517 Files: fm{ 2518 "config/config.go": "package config\n type SystemConfig struct {}", // Local package should be promoted over shorter package 2519 "main.go": input, 2520 }, 2521 }, 2522 }, 2523 }.processTest(t, "mycompany.net/tool", "main.go", nil, nil, want) 2524 } 2525 2526 // Tests FindImportInLocalGoFiles looks at the import lines for other Go files in the 2527 // local directory, since the user is likely to import the same packages in the current 2528 // Go file. If an import is found that satisfies the need, it should be used over the 2529 // standard library. 2530 // https://golang.org/issues/17557 2531 func TestFindImportInLocalGoFiles(t *testing.T) { 2532 const input = `package main 2533 var _ = &bytes.Buffer{}` 2534 2535 const want = `package main 2536 2537 import "bytes.net/bytes" 2538 2539 var _ = &bytes.Buffer{} 2540 ` 2541 testConfig{ 2542 modules: []packagestest.Module{ 2543 { 2544 Name: "mycompany.net/tool", 2545 Files: fm{ 2546 "io.go": "package main\n import \"bytes.net/bytes\"\n var _ = &bytes.Buffer{}", // Contains package import that will cause stdlib to be ignored 2547 "main.go": input, 2548 }, 2549 }, 2550 { 2551 Name: "bytes.net/bytes", 2552 Files: fm{"bytes.go": "package bytes\n type Buffer struct {}"}, // Should be selected over standard library 2553 }, 2554 }, 2555 }.processTest(t, "mycompany.net/tool", "main.go", nil, nil, want) 2556 } 2557 2558 func TestInMemoryFile(t *testing.T) { 2559 const input = `package main 2560 var _ = &bytes.Buffer{}` 2561 2562 const want = `package main 2563 2564 import "bytes" 2565 2566 var _ = &bytes.Buffer{} 2567 ` 2568 testConfig{ 2569 module: packagestest.Module{ 2570 Name: "foo.com", 2571 Files: fm{"x.go": "package x\n"}, 2572 }, 2573 }.processTest(t, "foo.com", "x.go", []byte(input), nil, want) 2574 } 2575 2576 func TestImportNoGoFiles(t *testing.T) { 2577 const input = `package main 2578 var _ = &bytes.Buffer{}` 2579 2580 const want = `package main 2581 2582 import "bytes" 2583 2584 var _ = &bytes.Buffer{} 2585 ` 2586 testConfig{ 2587 module: packagestest.Module{ 2588 Name: "mycompany.net", 2589 }, 2590 }.test(t, func(t *goimportTest) { 2591 buf, err := t.processNonModule("mycompany.net/tool/main.go", []byte(input), nil) 2592 if err != nil { 2593 t.Fatalf("Process() = %v", err) 2594 } 2595 if string(buf) != want { 2596 t.Errorf("Got:\n%s\nWant:\n%s", buf, want) 2597 } 2598 }) 2599 2600 } 2601 2602 // Ensures a token as large as 500000 bytes can be handled 2603 // https://golang.org/issues/18201 2604 func TestProcessLargeToken(t *testing.T) { 2605 largeString := strings.Repeat("x", 500000) 2606 2607 input := `package testimports 2608 2609 import ( 2610 "bytes" 2611 ) 2612 2613 const s = fmt.Sprintf("%s", "` + largeString + `") 2614 var _ = bytes.Buffer{} 2615 2616 // end 2617 ` 2618 2619 want := `package testimports 2620 2621 import ( 2622 "bytes" 2623 "fmt" 2624 ) 2625 2626 const s = fmt.Sprintf("%s", "` + largeString + `") 2627 2628 var _ = bytes.Buffer{} 2629 2630 // end 2631 ` 2632 2633 testConfig{ 2634 module: packagestest.Module{ 2635 Name: "foo.com", 2636 Files: fm{"foo.go": input}, 2637 }, 2638 }.processTest(t, "foo.com", "foo.go", nil, nil, want) 2639 } 2640 2641 // Tests that an external test package will import the package under test if it 2642 // also uses symbols exported only in test files. 2643 // https://golang.org/issues/29979 2644 func TestExternalTest(t *testing.T) { 2645 const input = `package a_test 2646 func TestX() { 2647 a.X() 2648 a.Y() 2649 } 2650 ` 2651 const want = `package a_test 2652 2653 import "foo.com/a" 2654 2655 func TestX() { 2656 a.X() 2657 a.Y() 2658 } 2659 ` 2660 2661 testConfig{ 2662 modules: []packagestest.Module{ 2663 { 2664 Name: "foo.com/a", 2665 Files: fm{ 2666 "a.go": "package a\n func X() {}", 2667 "export_test.go": "package a\n func Y() {}", 2668 "a_test.go": input, 2669 }, 2670 }, 2671 }, 2672 }.processTest(t, "foo.com/a", "a_test.go", nil, nil, want) 2673 } 2674 2675 // TestGetCandidates tests that get packages finds packages 2676 // with correct priorities. 2677 func TestGetCandidates(t *testing.T) { 2678 type res struct { 2679 relevance float64 2680 name, path string 2681 } 2682 want := []res{ 2683 {0, "bytes", "bytes"}, 2684 {0, "http", "net/http"}, 2685 {0, "rand", "crypto/rand"}, 2686 {0, "bar", "bar.com/bar"}, 2687 {0, "foo", "foo.com/foo"}, 2688 } 2689 2690 testConfig{ 2691 modules: []packagestest.Module{ 2692 { 2693 Name: "bar.com", 2694 Files: fm{"bar/bar.go": "package bar\n"}, 2695 }, 2696 { 2697 Name: "foo.com", 2698 Files: fm{"foo/foo.go": "package foo\n"}, 2699 }, 2700 }, 2701 }.test(t, func(t *goimportTest) { 2702 var mu sync.Mutex 2703 var got []res 2704 add := func(c ImportFix) { 2705 mu.Lock() 2706 defer mu.Unlock() 2707 for _, w := range want { 2708 if c.StmtInfo.ImportPath == w.path { 2709 got = append(got, res{c.Relevance, c.IdentName, c.StmtInfo.ImportPath}) 2710 } 2711 } 2712 } 2713 if err := GetAllCandidates(context.Background(), add, "", "x.go", "x", t.env); err != nil { 2714 t.Fatalf("GetAllCandidates() = %v", err) 2715 } 2716 // Sort, then clear out relevance so it doesn't mess up the DeepEqual. 2717 sort.Slice(got, func(i, j int) bool { 2718 ri, rj := got[i], got[j] 2719 if ri.relevance != rj.relevance { 2720 return ri.relevance > rj.relevance // Highest first. 2721 } 2722 return ri.name < rj.name 2723 }) 2724 for i := range got { 2725 got[i].relevance = 0 2726 } 2727 if !reflect.DeepEqual(want, got) { 2728 t.Errorf("wanted results in order %v, got %v", want, got) 2729 } 2730 }) 2731 } 2732 2733 func TestGetImportPaths(t *testing.T) { 2734 type res struct { 2735 relevance float64 2736 name, path string 2737 } 2738 want := []res{ 2739 {0, "http", "net/http"}, 2740 {0, "net", "net"}, 2741 {0, "neta", "neta.com/neta"}, 2742 } 2743 2744 testConfig{ 2745 modules: []packagestest.Module{ 2746 { 2747 Name: "neta.com", 2748 Files: fm{"neta/neta.go": "package neta\n"}, 2749 }, 2750 }, 2751 }.test(t, func(t *goimportTest) { 2752 var mu sync.Mutex 2753 var got []res 2754 add := func(c ImportFix) { 2755 mu.Lock() 2756 defer mu.Unlock() 2757 for _, w := range want { 2758 if c.StmtInfo.ImportPath == w.path { 2759 got = append(got, res{c.Relevance, c.IdentName, c.StmtInfo.ImportPath}) 2760 } 2761 } 2762 } 2763 if err := GetImportPaths(context.Background(), add, "ne", "x.go", "x", t.env); err != nil { 2764 t.Fatalf("GetImportPaths() = %v", err) 2765 } 2766 // Sort, then clear out relevance so it doesn't mess up the DeepEqual. 2767 sort.Slice(got, func(i, j int) bool { 2768 ri, rj := got[i], got[j] 2769 if ri.relevance != rj.relevance { 2770 return ri.relevance > rj.relevance // Highest first. 2771 } 2772 return ri.name < rj.name 2773 }) 2774 for i := range got { 2775 got[i].relevance = 0 2776 } 2777 if !reflect.DeepEqual(want, got) { 2778 t.Errorf("wanted results in order %v, got %v", want, got) 2779 } 2780 }) 2781 } 2782 2783 func TestGetPackageCompletions(t *testing.T) { 2784 type res struct { 2785 relevance float64 2786 name, path, symbol string 2787 } 2788 want := []res{ 2789 {0, "rand", "math/rand", "Seed"}, 2790 {0, "rand", "bar.com/rand", "Bar"}, 2791 } 2792 2793 testConfig{ 2794 modules: []packagestest.Module{ 2795 { 2796 Name: "bar.com", 2797 Files: fm{"rand/bar.go": "package rand\nvar Bar int\n"}, 2798 }, 2799 }, 2800 }.test(t, func(t *goimportTest) { 2801 var mu sync.Mutex 2802 var got []res 2803 add := func(c PackageExport) { 2804 mu.Lock() 2805 defer mu.Unlock() 2806 for _, csym := range c.Exports { 2807 for _, w := range want { 2808 if c.Fix.StmtInfo.ImportPath == w.path && csym == w.symbol { 2809 got = append(got, res{c.Fix.Relevance, c.Fix.IdentName, c.Fix.StmtInfo.ImportPath, csym}) 2810 } 2811 } 2812 } 2813 } 2814 if err := GetPackageExports(context.Background(), add, "rand", "x.go", "x", t.env); err != nil { 2815 t.Fatalf("getPackageCompletions() = %v", err) 2816 } 2817 // Sort, then clear out relevance so it doesn't mess up the DeepEqual. 2818 sort.Slice(got, func(i, j int) bool { 2819 ri, rj := got[i], got[j] 2820 if ri.relevance != rj.relevance { 2821 return ri.relevance > rj.relevance // Highest first. 2822 } 2823 return ri.name < rj.name 2824 }) 2825 for i := range got { 2826 got[i].relevance = 0 2827 } 2828 if !reflect.DeepEqual(want, got) { 2829 t.Errorf("wanted results in order %v, got %v", want, got) 2830 } 2831 }) 2832 } 2833 2834 // Tests #34895: process should not panic on concurrent calls. 2835 func TestConcurrentProcess(t *testing.T) { 2836 testConfig{ 2837 module: packagestest.Module{ 2838 Name: "foo.com", 2839 Files: fm{ 2840 "p/first.go": `package foo 2841 2842 func _() { 2843 fmt.Println() 2844 } 2845 `, 2846 "p/second.go": `package foo 2847 2848 import "fmt" 2849 2850 func _() { 2851 fmt.Println() 2852 imports.Bar() // not imported. 2853 } 2854 `, 2855 }, 2856 }, 2857 }.test(t, func(t *goimportTest) { 2858 var ( 2859 n = 10 2860 wg sync.WaitGroup 2861 ) 2862 wg.Add(n) 2863 for i := 0; i < n; i++ { 2864 go func() { 2865 defer wg.Done() 2866 _, err := t.process("foo.com", "p/first.go", nil, nil) 2867 if err != nil { 2868 t.Error(err) 2869 } 2870 }() 2871 } 2872 wg.Wait() 2873 }) 2874 } 2875 2876 func TestNonlocalDot(t *testing.T) { 2877 const input = `package main 2878 import ( 2879 "fmt" 2880 ) 2881 var _, _ = fmt.Sprintf, dot.Dot 2882 ` 2883 const want = `package main 2884 2885 import ( 2886 "fmt" 2887 "noninternet/dot.v1/dot" 2888 ) 2889 2890 var _, _ = fmt.Sprintf, dot.Dot 2891 ` 2892 testConfig{ 2893 modules: []packagestest.Module{ 2894 { 2895 Name: "golang.org/fake", 2896 Files: fm{"x.go": input}, 2897 }, 2898 { 2899 Name: "noninternet/dot.v1", 2900 Files: fm{ 2901 "dot/dot.go": "package dot\nfunc Dot(){}\n", 2902 }, 2903 }, 2904 }, 2905 gopathOnly: true, // our modules testing setup doesn't allow modules without dots. 2906 }.processTest(t, "golang.org/fake", "x.go", nil, nil, want) 2907 }