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