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.
18 var fset = token.NewFileSet()
20 func parse(t *testing.T, name, in string) *ast.File {
21 file, err := parser.ParseFile(fset, name, in, parser.ParseComments)
23 t.Fatalf("%s parse: %v", name, err)
28 func print(t *testing.T, name string, f *ast.File) string {
30 if err := format.Node(&buf, fset, f); err != nil {
31 t.Fatalf("%s gofmt: %v", name, err)
42 unchanged bool // Expect added/deleted return value to be false.
45 var addTests = []test{
47 name: "leave os alone",
147 name: "issue #19190",
176 name: "issue #19190 with existing grouped import packages",
215 name: "issue #19190 - match score is still respected",
236 name: "import into singular group",
252 name: "import into singular group with comment",
256 import /* why */ /* comment here? */ "os"
261 import /* why */ /* comment here? */ (
268 name: "import into group with leading comment",
273 // comment before bytes
282 // comment before bytes
291 renamedPkg: "fmtpkg",
307 name: "struct comment",
311 // This is a comment before a struct.
320 // This is a comment before a struct.
327 name: "issue 8729 import C",
346 name: "issue 8729 empty import",
364 name: "issue 8729 comment on package line",
366 in: `package main // comment
370 out: `package main // comment
378 name: "issue 8729 comment after package",
395 name: "issue 8729 comment before and on package line",
397 in: `// comment before
398 package main // comment on
402 out: `// comment before
403 package main // comment on
411 // Issue 9961: Match prefixes using path segments rather than bytes
435 // Issue 10337: Preserve comment position
456 name: "issue 10337 new import at the start",
475 name: "issue 10337 new import at the end",
493 // Issue 14075: Merge import declarations
512 name: "issue 14075 update position",
531 name: `issue 14075 ignore import "C"`,
554 name: `issue 14075 ignore adjacent import "C"`,
573 name: `issue 14075 ignore adjacent import "C" (without factored import)`,
592 name: `issue 14075 ignore single import "C"`,
607 name: `issue 17212 several single-import lines with shared prefix ending in a slash`,
624 name: `issue 17212 block imports lines with shared prefix ending in a slash`,
643 name: `issue 17213 many single-import lines`,
662 // Issue 28605: Add specified import, even if that import path is imported under another name
664 name: "issue 28605 add unnamed path",
686 name: "issue 28605 add pathpkg-renamed path",
687 renamedPkg: "pathpkg",
708 name: "issue 28605 add blank identifier path",
730 name: "issue 28605 add dot import path",
753 name: "duplicate import declarations, add existing one",
774 func TestAddImport(t *testing.T) {
775 for _, test := range addTests {
776 file := parse(t, test.name, test.in)
777 var before bytes.Buffer
778 ast.Fprint(&before, fset, file, nil)
779 added := AddNamedImport(fset, file, test.renamedPkg, test.pkg)
780 if got := print(t, test.name, file); got != test.out {
781 t.Errorf("first run: %s:\ngot: %s\nwant: %s", test.name, got, test.out)
782 var after bytes.Buffer
783 ast.Fprint(&after, fset, file, nil)
784 t.Logf("AST before:\n%s\nAST after:\n%s\n", before.String(), after.String())
786 if got, want := added, !test.unchanged; got != want {
787 t.Errorf("first run: %s: added = %v, want %v", test.name, got, want)
790 // AddNamedImport should be idempotent. Verify that by calling it again,
791 // expecting no change to the AST, and the returned added value to always be false.
792 added = AddNamedImport(fset, file, test.renamedPkg, test.pkg)
793 if got := print(t, test.name, file); got != test.out {
794 t.Errorf("second run: %s:\ngot: %s\nwant: %s", test.name, got, test.out)
796 if got, want := added, false; got != want {
797 t.Errorf("second run: %s: added = %v, want %v", test.name, got, want)
802 func TestDoubleAddImport(t *testing.T) {
803 file := parse(t, "doubleimport", "package main\n")
804 AddImport(fset, file, "os")
805 AddImport(fset, file, "bytes")
806 want := `package main
813 if got := print(t, "doubleimport", file); got != want {
814 t.Errorf("got: %s\nwant: %s", got, want)
818 func TestDoubleAddNamedImport(t *testing.T) {
819 file := parse(t, "doublenamedimport", "package main\n")
820 AddNamedImport(fset, file, "o", "os")
821 AddNamedImport(fset, file, "i", "io")
822 want := `package main
829 if got := print(t, "doublenamedimport", file); got != want {
830 t.Errorf("got: %s\nwant: %s", got, want)
834 // Part of issue 8729.
835 func TestDoubleAddImportWithDeclComment(t *testing.T) {
836 file := parse(t, "doubleimport", `package main
844 // The AddImport order here matters.
845 AddImport(fset, file, "golang.org/x/tools/go/ast/astutil")
846 AddImport(fset, file, "os")
847 want := `package main
850 "golang.org/x/tools/go/ast/astutil"
857 if got := print(t, "doubleimport_with_decl_comment", file); got != want {
858 t.Errorf("got: %s\nwant: %s", got, want)
862 var deleteTests = []test{
1033 name: "handle.raw.quote.imports",
1035 in: "package main\n\nimport `os`",
1177 // Issue #15432, #18051
1406 /* comment 2 */ "io"
1412 /* comment 2 */ "io"
1425 /* comment 2 */ i "io"
1431 /* comment 2 */ i "io"
1443 /* comment 2 */ f "fmt"
1462 f "fmt" /* comment 2 */
1514 // Issue 20229: MergeLine panic on weird input
1529 // Issue 28605: Delete specified import, even if that import path is imported under another name
1554 renamedPkg: "pathpkg",
1619 // Duplicate import declarations, all matching ones are deleted.
1659 func TestDeleteImport(t *testing.T) {
1660 for _, test := range deleteTests {
1661 file := parse(t, test.name, test.in)
1662 var before bytes.Buffer
1663 ast.Fprint(&before, fset, file, nil)
1664 deleted := DeleteNamedImport(fset, file, test.renamedPkg, test.pkg)
1665 if got := print(t, test.name, file); got != test.out {
1666 t.Errorf("first run: %s:\ngot: %s\nwant: %s", test.name, got, test.out)
1667 var after bytes.Buffer
1668 ast.Fprint(&after, fset, file, nil)
1669 t.Logf("AST before:\n%s\nAST after:\n%s\n", before.String(), after.String())
1671 if got, want := deleted, !test.unchanged; got != want {
1672 t.Errorf("first run: %s: deleted = %v, want %v", test.name, got, want)
1675 // DeleteNamedImport should be idempotent. Verify that by calling it again,
1676 // expecting no change to the AST, and the returned deleted value to always be false.
1677 deleted = DeleteNamedImport(fset, file, test.renamedPkg, test.pkg)
1678 if got := print(t, test.name, file); got != test.out {
1679 t.Errorf("second run: %s:\ngot: %s\nwant: %s", test.name, got, test.out)
1681 if got, want := deleted, false; got != want {
1682 t.Errorf("second run: %s: deleted = %v, want %v", test.name, got, want)
1687 func TestDeleteImportAfterAddImport(t *testing.T) {
1688 file := parse(t, "test", `package main
1692 if got, want := AddImport(fset, file, "fmt"), true; got != want {
1693 t.Errorf("AddImport: got: %v, want: %v", got, want)
1695 if got, want := DeleteImport(fset, file, "fmt"), true; got != want {
1696 t.Errorf("DeleteImport: got: %v, want: %v", got, want)
1700 type rewriteTest struct {
1708 var rewriteTests = []rewriteTest{
1712 dstPkg: "encoding/utf8",
1718 "utf8" // thanks ken
1724 "encoding/utf8" // thanks ken
1733 dstPkg: "encoding/asn1",
1776 var x = 1 // comment on x, not on url
1787 var x = 1 // comment on x, not on url
1803 var addr = flag.String("addr", ":1718", "http service address") // Q=17, R=18
1814 var addr = flag.String("addr", ":1718", "http service address") // Q=17, R=18
1819 func TestRewriteImport(t *testing.T) {
1820 for _, test := range rewriteTests {
1821 file := parse(t, test.name, test.in)
1822 RewriteImport(fset, file, test.srcPkg, test.dstPkg)
1823 if got := print(t, test.name, file); got != test.out {
1824 t.Errorf("%s:\ngot: %s\nwant: %s", test.name, got, test.out)
1829 var importsTests = []struct {
1835 name: "no packages",
1849 want: [][]string{{"fmt", "testing"}},
1852 name: "four groups",
1870 {"myproject/mylib1", "myproject/mylib2"},
1874 name: "multiple factored groups",
1898 func unquote(s string) string {
1899 res, err := strconv.Unquote(s)
1901 return "could_not_unquote"
1906 func TestImports(t *testing.T) {
1907 fset := token.NewFileSet()
1908 for _, test := range importsTests {
1909 f, err := parser.ParseFile(fset, "test.go", test.in, 0)
1911 t.Errorf("%s: %v", test.name, err)
1915 for _, group := range Imports(fset, f) {
1917 for _, spec := range group {
1918 b = append(b, unquote(spec.Path.Value))
1920 got = append(got, b)
1922 if !reflect.DeepEqual(got, test.want) {
1923 t.Errorf("Imports(%s)=%v, want %v", test.name, got, test.want)
1928 var usesImportTests = []struct {
1935 name: "no packages",
2006 path: "encoding/json",
2009 import "encoding/json"
2017 path: "encoding/json",
2020 import "encoding/json"
2026 path: "encoding/json",
2029 import "encoding/json"
2037 path: "encoding/json",
2040 import j "encoding/json"
2048 path: "encoding/json",
2051 import j "encoding/json"
2057 path: "encoding/json",
2060 import j "encoding/json"
2087 func TestUsesImport(t *testing.T) {
2088 fset := token.NewFileSet()
2089 for _, test := range usesImportTests {
2090 f, err := parser.ParseFile(fset, "test.go", test.in, 0)
2092 t.Errorf("%s: %v", test.name, err)
2095 got := UsesImport(f, test.path)
2096 if got != test.want {
2097 t.Errorf("UsesImport(%s)=%v, want %v", test.name, got, test.want)