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