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