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