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