Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201105173854-bc9fc8d8c4bc / internal / imports / fix_test.go
diff --git a/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201105173854-bc9fc8d8c4bc/internal/imports/fix_test.go b/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201105173854-bc9fc8d8c4bc/internal/imports/fix_test.go
new file mode 100644 (file)
index 0000000..005bf96
--- /dev/null
@@ -0,0 +1,2795 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package imports
+
+import (
+       "context"
+       "flag"
+       "fmt"
+       "go/build"
+       "io/ioutil"
+       "log"
+       "path/filepath"
+       "reflect"
+       "runtime"
+       "sort"
+       "strings"
+       "sync"
+       "testing"
+
+       "golang.org/x/tools/go/packages/packagestest"
+       "golang.org/x/tools/internal/gocommand"
+)
+
+var testDebug = flag.Bool("debug", false, "enable debug output")
+
+var tests = []struct {
+       name       string
+       formatOnly bool
+       in, out    string
+}{
+       // Adding an import to an existing parenthesized import
+       {
+               name: "factored_imports_add",
+               in: `package foo
+import (
+  "fmt"
+)
+func bar() {
+var b bytes.Buffer
+fmt.Println(b.String())
+}
+`,
+               out: `package foo
+
+import (
+       "bytes"
+       "fmt"
+)
+
+func bar() {
+       var b bytes.Buffer
+       fmt.Println(b.String())
+}
+`,
+       },
+
+       // Adding an import to an existing parenthesized import,
+       // verifying it goes into the first section.
+       {
+               name: "factored_imports_add_first_sec",
+               in: `package foo
+import (
+  "fmt"
+
+  "github.com/golang/snappy"
+)
+func bar() {
+var b bytes.Buffer
+_ = snappy.ErrCorrupt
+fmt.Println(b.String())
+}
+`,
+               out: `package foo
+
+import (
+       "bytes"
+       "fmt"
+
+       "github.com/golang/snappy"
+)
+
+func bar() {
+       var b bytes.Buffer
+       _ = snappy.ErrCorrupt
+       fmt.Println(b.String())
+}
+`,
+       },
+
+       // Adding an import to an existing parenthesized import,
+       // verifying it goes into the first section. (test 2)
+       {
+               name: "factored_imports_add_first_sec_2",
+               in: `package foo
+import (
+  "fmt"
+
+  "github.com/golang/snappy"
+)
+func bar() {
+_ = math.NaN
+_ = fmt.Sprintf
+_ = snappy.ErrCorrupt
+}
+`,
+               out: `package foo
+
+import (
+       "fmt"
+       "math"
+
+       "github.com/golang/snappy"
+)
+
+func bar() {
+       _ = math.NaN
+       _ = fmt.Sprintf
+       _ = snappy.ErrCorrupt
+}
+`,
+       },
+
+       // Adding a new import line, without parens
+       {
+               name: "add_import_section",
+               in: `package foo
+func bar() {
+var b bytes.Buffer
+}
+`,
+               out: `package foo
+
+import "bytes"
+
+func bar() {
+       var b bytes.Buffer
+}
+`,
+       },
+
+       // Adding two new imports, which should make a parenthesized import decl.
+       {
+               name: "add_import_paren_section",
+               in: `package foo
+func bar() {
+_, _ := bytes.Buffer, zip.NewReader
+}
+`,
+               out: `package foo
+
+import (
+       "archive/zip"
+       "bytes"
+)
+
+func bar() {
+       _, _ := bytes.Buffer, zip.NewReader
+}
+`,
+       },
+
+       // Make sure we don't add things twice
+       {
+               name: "no_double_add",
+               in: `package foo
+func bar() {
+_, _ := bytes.Buffer, bytes.NewReader
+}
+`,
+               out: `package foo
+
+import "bytes"
+
+func bar() {
+       _, _ := bytes.Buffer, bytes.NewReader
+}
+`,
+       },
+
+       // Make sure we don't add packages that don't have the right exports
+       {
+               name: "no_mismatched_add",
+               in: `package foo
+
+func bar() {
+       _ := bytes.NonexistentSymbol
+}
+`,
+               out: `package foo
+
+func bar() {
+       _ := bytes.NonexistentSymbol
+}
+`,
+       },
+
+       // Remove unused imports, 1 of a factored block
+       {
+               name: "remove_unused_1_of_2",
+               in: `package foo
+import (
+"bytes"
+"fmt"
+)
+
+func bar() {
+_, _ := bytes.Buffer, bytes.NewReader
+}
+`,
+               out: `package foo
+
+import (
+       "bytes"
+)
+
+func bar() {
+       _, _ := bytes.Buffer, bytes.NewReader
+}
+`,
+       },
+
+       // Remove unused imports, 2 of 2
+       {
+               name: "remove_unused_2_of_2",
+               in: `package foo
+import (
+"bytes"
+"fmt"
+)
+
+func bar() {
+}
+`,
+               out: `package foo
+
+func bar() {
+}
+`,
+       },
+
+       // Remove unused imports, 1 of 1
+       {
+               name: "remove_unused_1_of_1",
+               in: `package foo
+
+import "fmt"
+
+func bar() {
+}
+`,
+               out: `package foo
+
+func bar() {
+}
+`,
+       },
+
+       // Don't remove empty imports.
+       {
+               name: "dont_remove_empty_imports",
+               in: `package foo
+import (
+_ "image/png"
+_ "image/jpeg"
+)
+`,
+               out: `package foo
+
+import (
+       _ "image/jpeg"
+       _ "image/png"
+)
+`,
+       },
+
+       // Don't remove dot imports.
+       {
+               name: "dont_remove_dot_imports",
+               in: `package foo
+import (
+. "foo"
+. "bar"
+)
+`,
+               out: `package foo
+
+import (
+       . "bar"
+       . "foo"
+)
+`,
+       },
+
+       // Skip refs the parser can resolve.
+       {
+               name: "skip_resolved_refs",
+               in: `package foo
+
+func f() {
+       type t struct{ Println func(string) }
+       fmt := t{Println: func(string) {}}
+       fmt.Println("foo")
+}
+`,
+               out: `package foo
+
+func f() {
+       type t struct{ Println func(string) }
+       fmt := t{Println: func(string) {}}
+       fmt.Println("foo")
+}
+`,
+       },
+
+       // Do not add a package we already have a resolution for.
+       {
+               name: "skip_template",
+               in: `package foo
+
+import "html/template"
+
+func f() { t = template.New("sometemplate") }
+`,
+               out: `package foo
+
+import "html/template"
+
+func f() { t = template.New("sometemplate") }
+`,
+       },
+
+       // Don't touch cgo
+       {
+               name: "cgo",
+               in: `package foo
+
+/*
+#include <foo.h>
+*/
+import "C"
+`,
+               out: `package foo
+
+/*
+#include <foo.h>
+*/
+import "C"
+`,
+       },
+
+       // Put some things in their own section
+       {
+               name: "make_sections",
+               in: `package foo
+
+import (
+"os"
+)
+
+func foo () {
+_, _ = os.Args, fmt.Println
+_, _ = snappy.ErrCorrupt, p.P
+}
+`,
+               out: `package foo
+
+import (
+       "fmt"
+       "os"
+
+       "github.com/golang/snappy"
+       "rsc.io/p"
+)
+
+func foo() {
+       _, _ = os.Args, fmt.Println
+       _, _ = snappy.ErrCorrupt, p.P
+}
+`,
+       },
+       // Merge import blocks, even when no additions are required.
+       {
+               name: "merge_import_blocks_no_fix",
+               in: `package foo
+
+import (
+       "fmt"
+)
+import "os"
+
+import (
+       "rsc.io/p"
+)
+
+var _, _ = os.Args, fmt.Println
+var _, _ = snappy.ErrCorrupt, p.P
+`,
+               out: `package foo
+
+import (
+       "fmt"
+       "os"
+
+       "github.com/golang/snappy"
+       "rsc.io/p"
+)
+
+var _, _ = os.Args, fmt.Println
+var _, _ = snappy.ErrCorrupt, p.P
+`,
+       },
+       // Delete existing empty import block
+       {
+               name: "delete_empty_import_block",
+               in: `package foo
+
+import ()
+`,
+               out: `package foo
+`,
+       },
+
+       // Use existing empty import block
+       {
+               name: "use_empty_import_block",
+               in: `package foo
+
+import ()
+
+func f() {
+       _ = fmt.Println
+}
+`,
+               out: `package foo
+
+import "fmt"
+
+func f() {
+       _ = fmt.Println
+}
+`,
+       },
+
+       // Blank line before adding new section.
+       {
+               name: "blank_line_before_new_group",
+               in: `package foo
+
+import (
+       "fmt"
+       "net"
+)
+
+func f() {
+       _ = net.Dial
+       _ = fmt.Printf
+       _ = snappy.ErrCorrupt
+}
+`,
+               out: `package foo
+
+import (
+       "fmt"
+       "net"
+
+       "github.com/golang/snappy"
+)
+
+func f() {
+       _ = net.Dial
+       _ = fmt.Printf
+       _ = snappy.ErrCorrupt
+}
+`,
+       },
+
+       // Blank line between standard library and third-party stuff.
+       {
+               name: "blank_line_separating_std_and_third_party",
+               in: `package foo
+
+import (
+       "github.com/golang/snappy"
+       "fmt"
+       "net"
+)
+
+func f() {
+       _ = net.Dial
+       _ = fmt.Printf
+       _ = snappy.Foo
+}
+`,
+               out: `package foo
+
+import (
+       "fmt"
+       "net"
+
+       "github.com/golang/snappy"
+)
+
+func f() {
+       _ = net.Dial
+       _ = fmt.Printf
+       _ = snappy.Foo
+}
+`,
+       },
+
+       // golang.org/issue/6884
+       {
+               name: "new_imports_before_comment",
+               in: `package main
+
+// A comment
+func main() {
+       fmt.Println("Hello, world")
+}
+`,
+               out: `package main
+
+import "fmt"
+
+// A comment
+func main() {
+       fmt.Println("Hello, world")
+}
+`,
+       },
+
+       // golang.org/issue/7132
+       {
+               name: "new_section_for_dotless_import",
+               in: `package main
+
+import (
+"fmt"
+
+"gu"
+"manypackages.com/packagea"
+)
+
+var (
+a = packagea.A
+b = gu.A
+c = fmt.Printf
+)
+`,
+               out: `package main
+
+import (
+       "fmt"
+
+       "gu"
+
+       "manypackages.com/packagea"
+)
+
+var (
+       a = packagea.A
+       b = gu.A
+       c = fmt.Printf
+)
+`,
+       },
+
+       {
+               name: "fragment_with_main",
+               in:   `func main(){fmt.Println("Hello, world")}`,
+               out: `package main
+
+import "fmt"
+
+func main() { fmt.Println("Hello, world") }
+`,
+       },
+
+       {
+               name: "fragment_without_main",
+               in:   `func notmain(){fmt.Println("Hello, world")}`,
+               out: `import "fmt"
+
+func notmain() { fmt.Println("Hello, world") }`,
+       },
+
+       // Remove first import within in a 2nd/3rd/4th/etc. section.
+       // golang.org/issue/7679
+       {
+               name: "remove_first_import_in_section",
+               in: `package main
+
+import (
+       "fmt"
+
+       "manypackages.com/packagea"
+       "manypackages.com/packageb"
+)
+
+func main() {
+       var _ = fmt.Println
+       //var _ = packagea.A
+       var _ = packageb.B
+}
+`,
+               out: `package main
+
+import (
+       "fmt"
+
+       "manypackages.com/packageb"
+)
+
+func main() {
+       var _ = fmt.Println
+       //var _ = packagea.A
+       var _ = packageb.B
+}
+`,
+       },
+
+       // Blank line can be added before all types of import declarations.
+       // golang.org/issue/7866
+       {
+               name: "new_section_for_all_kinds_of_imports",
+               in: `package main
+
+import (
+       "fmt"
+       renamed_packagea "manypackages.com/packagea"
+
+       . "manypackages.com/packageb"
+       "io"
+
+       _ "manypackages.com/packagec"
+       "strings"
+)
+
+var _, _, _, _, _ = fmt.Errorf, io.Copy, strings.Contains, renamed_packagea.A, B
+`,
+               out: `package main
+
+import (
+       "fmt"
+
+       renamed_packagea "manypackages.com/packagea"
+
+       "io"
+
+       . "manypackages.com/packageb"
+
+       "strings"
+
+       _ "manypackages.com/packagec"
+)
+
+var _, _, _, _, _ = fmt.Errorf, io.Copy, strings.Contains, renamed_packagea.A, B
+`,
+       },
+
+       // Non-idempotent comment formatting
+       // golang.org/issue/8035
+       {
+               name: "comments_formatted",
+               in: `package main
+
+import (
+       "fmt"                     // A
+       "go/ast"                  // B
+       _ "manypackages.com/packagec"    // C
+)
+
+func main() { _, _ = fmt.Print, ast.Walk }
+`,
+               out: `package main
+
+import (
+       "fmt"    // A
+       "go/ast" // B
+
+       _ "manypackages.com/packagec" // C
+)
+
+func main() { _, _ = fmt.Print, ast.Walk }
+`,
+       },
+
+       // Failure to delete all duplicate imports
+       // golang.org/issue/8459
+       {
+               name: "remove_duplicates",
+               in: `package main
+
+import (
+       "fmt"
+       "log"
+       "log"
+       "math"
+)
+
+func main() { fmt.Println("pi:", math.Pi) }
+`,
+               out: `package main
+
+import (
+       "fmt"
+       "math"
+)
+
+func main() { fmt.Println("pi:", math.Pi) }
+`,
+       },
+
+       // Too aggressive prefix matching
+       // golang.org/issue/9961
+       {
+               name: "no_extra_groups",
+               in: `package p
+
+import (
+       "zip"
+
+       "rsc.io/p"
+)
+
+var (
+       _ = fmt.Print
+       _ = zip.Store
+       _ p.P
+       _ = regexp.Compile
+)
+`,
+               out: `package p
+
+import (
+       "fmt"
+       "regexp"
+       "zip"
+
+       "rsc.io/p"
+)
+
+var (
+       _ = fmt.Print
+       _ = zip.Store
+       _ p.P
+       _ = regexp.Compile
+)
+`,
+       },
+
+       // Unused named import is mistaken for unnamed import
+       // golang.org/issue/8149
+       {
+               name: "named_import_doesnt_provide_package_name",
+               in: `package main
+
+import foo "fmt"
+
+func main() { fmt.Println() }
+`,
+               out: `package main
+
+import "fmt"
+
+func main() { fmt.Println() }
+`,
+       },
+
+       // Unused named import is mistaken for unnamed import
+       // golang.org/issue/8149
+       {
+               name: "unused_named_import_removed",
+               in: `package main
+
+import (
+       "fmt"
+       x "fmt"
+)
+
+func main() { fmt.Println() }
+`,
+               out: `package main
+
+import (
+       "fmt"
+)
+
+func main() { fmt.Println() }
+`,
+       },
+
+       {
+               name: "ignore_unexported_identifier",
+               in: `package main
+var _ = fmt.unexported`,
+               out: `package main
+
+var _ = fmt.unexported
+`,
+       },
+
+       // FormatOnly
+       {
+               name:       "formatonly_works",
+               formatOnly: true,
+               in: `package main
+
+import (
+"fmt"
+"manypackages.com/packagea"
+)
+
+func main() {}
+`,
+               out: `package main
+
+import (
+       "fmt"
+
+       "manypackages.com/packagea"
+)
+
+func main() {}
+`,
+       },
+
+       {
+               name: "preserve_import_group",
+               in: `package p
+
+import (
+       "bytes"
+       "fmt"
+)
+
+var _ = fmt.Sprintf
+`,
+               out: `package p
+
+import (
+       "fmt"
+)
+
+var _ = fmt.Sprintf
+`,
+       },
+       {
+               name: "import_grouping_not_path_dependent_no_groups",
+               in: `package main
+
+import (
+       "time"
+)
+
+func main() {
+       _ = snappy.ErrCorrupt
+       _ = p.P
+       _ = time.Parse
+}
+`,
+               out: `package main
+
+import (
+       "time"
+
+       "github.com/golang/snappy"
+       "rsc.io/p"
+)
+
+func main() {
+       _ = snappy.ErrCorrupt
+       _ = p.P
+       _ = time.Parse
+}
+`,
+       },
+
+       {
+               name: "import_grouping_not_path_dependent_existing_group",
+               in: `package main
+
+import (
+       "time"
+
+       "github.com/golang/snappy"
+)
+
+func main() {
+       _ = snappy.ErrCorrupt
+       _ = p.P
+       _ = time.Parse
+}
+`,
+               out: `package main
+
+import (
+       "time"
+
+       "github.com/golang/snappy"
+       "rsc.io/p"
+)
+
+func main() {
+       _ = snappy.ErrCorrupt
+       _ = p.P
+       _ = time.Parse
+}
+`,
+       },
+
+       // golang.org/issue/12097
+       {
+               name: "package_statement_insertion_preserves_comments",
+               in: `// a
+// b
+// c
+
+func main() {
+    _ = fmt.Println
+}`,
+               out: `package main
+
+import "fmt"
+
+// a
+// b
+// c
+
+func main() {
+       _ = fmt.Println
+}
+`,
+       },
+
+       {
+               name: "import_comment_stays_on_import",
+               in: `package main
+
+import (
+       "math" // fun
+)
+
+func main() {
+       x := math.MaxInt64
+       fmt.Println(strings.Join(",", []string{"hi"}), x)
+}`,
+               out: `package main
+
+import (
+       "fmt"
+       "math" // fun
+       "strings"
+)
+
+func main() {
+       x := math.MaxInt64
+       fmt.Println(strings.Join(",", []string{"hi"}), x)
+}
+`,
+       },
+
+       {
+               name: "no_blank_after_comment",
+               in: `package main
+
+import (
+       _ "io"
+       _ "net/http"
+       _ "net/http/pprof" // install the pprof http handlers
+       _ "strings"
+)
+
+func main() {
+}
+`,
+               out: `package main
+
+import (
+       _ "io"
+       _ "net/http"
+       _ "net/http/pprof" // install the pprof http handlers
+       _ "strings"
+)
+
+func main() {
+}
+`,
+       },
+
+       {
+               name: "no_blank_after_comment_reordered",
+               in: `package main
+
+import (
+       _ "io"
+       _ "net/http/pprof" // install the pprof http handlers
+       _ "net/http"
+       _ "strings"
+)
+
+func main() {
+}
+`,
+               out: `package main
+
+import (
+       _ "io"
+       _ "net/http"
+       _ "net/http/pprof" // install the pprof http handlers
+       _ "strings"
+)
+
+func main() {
+}
+`,
+       },
+
+       {
+               name: "no_blank_after_comment_unnamed",
+               in: `package main
+
+import (
+       "encoding/json"
+       "io"
+       "net/http"
+       _ "net/http/pprof" // install the pprof http handlers
+       "strings"
+
+       "manypackages.com/packagea"
+)
+
+func main() {
+       _ = strings.ToUpper("hello")
+       _ = io.EOF
+       var (
+               _ json.Number
+               _ *http.Request
+               _ packagea.A
+       )
+}
+`,
+               out: `package main
+
+import (
+       "encoding/json"
+       "io"
+       "net/http"
+       _ "net/http/pprof" // install the pprof http handlers
+       "strings"
+
+       "manypackages.com/packagea"
+)
+
+func main() {
+       _ = strings.ToUpper("hello")
+       _ = io.EOF
+       var (
+               _ json.Number
+               _ *http.Request
+               _ packagea.A
+       )
+}
+`,
+       },
+
+       {
+               name: "blank_after_package_statement_with_comment",
+               in: `package p // comment
+
+import "math"
+
+var _ = fmt.Printf
+`,
+               out: `package p // comment
+
+import "fmt"
+
+var _ = fmt.Printf
+`,
+       },
+
+       {
+               name: "blank_after_package_statement_no_comment",
+               in: `package p
+
+import "math"
+
+var _ = fmt.Printf
+`,
+               out: `package p
+
+import "fmt"
+
+var _ = fmt.Printf
+`,
+       },
+
+       {
+               name: "cryptorand_preferred_easy_possible",
+               in: `package p
+
+var _ = rand.Read
+`,
+               out: `package p
+
+import "crypto/rand"
+
+var _ = rand.Read
+`,
+       },
+
+       {
+               name: "cryptorand_preferred_easy_impossible",
+               in: `package p
+
+var _ = rand.NewZipf
+`,
+               out: `package p
+
+import "math/rand"
+
+var _ = rand.NewZipf
+`,
+       },
+
+       {
+               name: "cryptorand_preferred_complex_possible",
+               in: `package p
+
+var _, _ = rand.Read, rand.Prime
+`,
+               out: `package p
+
+import "crypto/rand"
+
+var _, _ = rand.Read, rand.Prime
+`,
+       },
+
+       {
+               name: "cryptorand_preferred_complex_impossible",
+               in: `package p
+
+var _, _ = rand.Read, rand.NewZipf
+`,
+               out: `package p
+
+import "math/rand"
+
+var _, _ = rand.Read, rand.NewZipf
+`,
+       },
+}
+
+func TestSimpleCases(t *testing.T) {
+       const localPrefix = "local.com,github.com/local"
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       testConfig{
+                               modules: []packagestest.Module{
+                                       {
+                                               Name:  "golang.org/fake",
+                                               Files: fm{"x.go": tt.in},
+                                       },
+                                       // Skeleton non-stdlib packages for use during testing.
+                                       // Each includes one arbitrary symbol, e.g. the first declaration in the first file.
+                                       // Try not to add more without a good reason.
+                                       // DO NOT USE PACKAGES NOT LISTED HERE -- they will be downloaded!
+                                       {
+                                               Name:  "rsc.io",
+                                               Files: fm{"p/x.go": "package p\nfunc P(){}\n"},
+                                       },
+                                       {
+                                               Name:  "github.com/golang/snappy",
+                                               Files: fm{"x.go": "package snappy\nvar ErrCorrupt error\n"},
+                                       },
+                                       {
+                                               Name: "manypackages.com",
+                                               Files: fm{
+                                                       "packagea/x.go": "package packagea\nfunc A(){}\n",
+                                                       "packageb/x.go": "package packageb\nfunc B(){}\n",
+                                                       "packagec/x.go": "package packagec\nfunc C(){}\n",
+                                                       "packaged/x.go": "package packaged\nfunc D(){}\n",
+                                               },
+                                       },
+                                       {
+                                               Name:  "local.com",
+                                               Files: fm{"foo/x.go": "package foo\nfunc Foo(){}\n"},
+                                       },
+                                       {
+                                               Name:  "github.com/local",
+                                               Files: fm{"bar/x.go": "package bar\nfunc Bar(){}\n"},
+                                       },
+                               },
+                       }.test(t, func(t *goimportTest) {
+                               options := &Options{
+                                       LocalPrefix: localPrefix,
+                                       TabWidth:    8,
+                                       TabIndent:   true,
+                                       Comments:    true,
+                                       Fragment:    true,
+                                       FormatOnly:  tt.formatOnly,
+                               }
+                               t.assertProcessEquals("golang.org/fake", "x.go", nil, options, tt.out)
+                       })
+
+               })
+       }
+}
+
+func TestAppengine(t *testing.T) {
+       const input = `package p
+
+var _, _, _ = fmt.Printf, appengine.Main, datastore.ErrInvalidEntityType
+`
+
+       const want = `package p
+
+import (
+       "fmt"
+
+       "appengine"
+       "appengine/datastore"
+)
+
+var _, _, _ = fmt.Printf, appengine.Main, datastore.ErrInvalidEntityType
+`
+
+       testConfig{
+               gopathOnly: true, // can't create a module named appengine, so no module tests.
+               modules: []packagestest.Module{
+                       {
+                               Name:  "golang.org/fake",
+                               Files: fm{"x.go": input},
+                       },
+                       {
+                               Name: "appengine",
+                               Files: fm{
+                                       "x.go":           "package appengine\nfunc Main(){}\n",
+                                       "datastore/x.go": "package datastore\nvar ErrInvalidEntityType error\n",
+                               },
+                       },
+               },
+       }.processTest(t, "golang.org/fake", "x.go", nil, nil, want)
+}
+
+func TestReadFromFilesystem(t *testing.T) {
+       tests := []struct {
+               name    string
+               in, out string
+       }{
+               {
+                       name: "works",
+                       in: `package foo
+func bar() {
+fmt.Println("hi")
+}
+`,
+                       out: `package foo
+
+import "fmt"
+
+func bar() {
+       fmt.Println("hi")
+}
+`,
+               },
+               {
+                       name: "missing_package",
+                       in: `
+func bar() {
+fmt.Println("hi")
+}
+`,
+                       out: `
+import "fmt"
+
+func bar() {
+       fmt.Println("hi")
+}
+`,
+               },
+       }
+
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       options := &Options{
+                               TabWidth:  8,
+                               TabIndent: true,
+                               Comments:  true,
+                               Fragment:  true,
+                       }
+                       testConfig{
+                               module: packagestest.Module{
+                                       Name:  "golang.org/fake",
+                                       Files: fm{"x.go": tt.in},
+                               },
+                       }.processTest(t, "golang.org/fake", "x.go", nil, options, tt.out)
+               })
+       }
+
+}
+
+// Test support for packages in GOPATH that are actually symlinks.
+// Also test that a symlink loop does not block the process.
+func TestImportSymlinks(t *testing.T) {
+       switch runtime.GOOS {
+       case "windows", "plan9":
+               t.Skipf("skipping test on %q as there are no symlinks", runtime.GOOS)
+       }
+
+       const input = `package p
+
+var (
+       _ = fmt.Print
+       _ = mypkg.Foo
+)
+`
+       const want = `package p
+
+import (
+       "fmt"
+
+       "golang.org/fake/x/y/mypkg"
+)
+
+var (
+       _ = fmt.Print
+       _ = mypkg.Foo
+)
+`
+
+       testConfig{
+               module: packagestest.Module{
+                       Name: "golang.org/fake",
+                       Files: fm{
+                               "target/f.go":                "package mypkg\nvar Foo = 123\n",
+                               "x/y/mypkg":                  packagestest.Symlink("../../target"), // valid symlink
+                               "x/y/apkg":                   packagestest.Symlink(".."),           // symlink loop
+                               "myotherpackage/toformat.go": input,
+                       },
+               },
+       }.processTest(t, "golang.org/fake", "myotherpackage/toformat.go", nil, nil, want)
+}
+
+func TestImportSymlinksWithIgnore(t *testing.T) {
+       switch runtime.GOOS {
+       case "windows", "plan9":
+               t.Skipf("skipping test on %q as there are no symlinks", runtime.GOOS)
+       }
+
+       const input = `package p
+
+var (
+       _ = fmt.Print
+       _ = mypkg.Foo
+)
+`
+       const want = `package p
+
+import "fmt"
+
+var (
+       _ = fmt.Print
+       _ = mypkg.Foo
+)
+`
+
+       testConfig{
+               gopathOnly: true,
+               module: packagestest.Module{
+                       Name: "golang.org/fake",
+                       Files: fm{
+                               "target/f.go":            "package mypkg\nvar Foo = 123\n",
+                               "x/y/mypkg":              packagestest.Symlink("../../target"), // valid symlink
+                               "x/y/apkg":               packagestest.Symlink(".."),           // symlink loop
+                               "myotherpkg/toformat.go": input,
+                               "../../.goimportsignore": "golang.org/fake/x/y/mypkg\n",
+                       },
+               },
+       }.processTest(t, "golang.org/fake", "myotherpkg/toformat.go", nil, nil, want)
+}
+
+// Test for x/y/v2 convention for package y.
+func TestModuleVersion(t *testing.T) {
+       const input = `package p
+
+import (
+       "fmt"
+
+       "github.com/foo/v2"
+)
+
+var (
+       _ = fmt.Print
+       _ = foo.Foo
+)
+`
+
+       testConfig{
+               modules: []packagestest.Module{
+                       {
+                               Name:  "mypkg.com/outpkg",
+                               Files: fm{"toformat.go": input},
+                       },
+                       {
+                               Name:  "github.com/foo/v2",
+                               Files: fm{"x.go": "package foo\n func Foo(){}\n"},
+                       },
+               },
+       }.processTest(t, "mypkg.com/outpkg", "toformat.go", nil, nil, input)
+}
+
+// Test for correctly identifying the name of a vendored package when it
+// differs from its directory name. In this test, the import line
+// "mypkg.com/mypkg_v1" would be removed if goimports wasn't able to detect
+// that the package name is "mypkg".
+func TestVendorPackage(t *testing.T) {
+       const input = `package p
+import (
+       "fmt"
+       "mypkg.com/mypkg_v1"
+)
+var _, _ = fmt.Print, mypkg.Foo
+`
+
+       const want = `package p
+
+import (
+       "fmt"
+
+       mypkg "mypkg.com/mypkg_v1"
+)
+
+var _, _ = fmt.Print, mypkg.Foo
+`
+
+       testConfig{
+               gopathOnly: true,
+               module: packagestest.Module{
+                       Name: "mypkg.com/outpkg",
+                       Files: fm{
+                               "vendor/mypkg.com/mypkg_v1/f.go": "package mypkg\nvar Foo = 123\n",
+                               "toformat.go":                    input,
+                       },
+               },
+       }.processTest(t, "mypkg.com/outpkg", "toformat.go", nil, nil, want)
+}
+
+func TestInternal(t *testing.T) {
+       const input = `package bar
+
+var _ = race.Acquire
+`
+       const importAdded = `package bar
+
+import "foo.com/internal/race"
+
+var _ = race.Acquire
+`
+
+       // Packages under the same directory should be able to use internal packages.
+       testConfig{
+               module: packagestest.Module{
+                       Name: "foo.com",
+                       Files: fm{
+                               "internal/race/x.go": "package race\n func Acquire(){}\n",
+                               "bar/x.go":           input,
+                       },
+               },
+       }.processTest(t, "foo.com", "bar/x.go", nil, nil, importAdded)
+
+       // Packages outside the same directory should not.
+       testConfig{
+               modules: []packagestest.Module{
+                       {
+                               Name:  "foo.com",
+                               Files: fm{"internal/race/x.go": "package race\n func Acquire(){}\n"},
+                       },
+                       {
+                               Name:  "bar.com",
+                               Files: fm{"x.go": input},
+                       },
+               },
+       }.processTest(t, "bar.com", "x.go", nil, nil, input)
+}
+
+func TestProcessVendor(t *testing.T) {
+       const input = `package p
+
+var _ = hpack.HuffmanDecode
+`
+       const want = `package p
+
+import "golang.org/x/net/http2/hpack"
+
+var _ = hpack.HuffmanDecode
+`
+       testConfig{
+               gopathOnly: true,
+               module: packagestest.Module{
+                       Name: "foo.com",
+                       Files: fm{
+                               "vendor/golang.org/x/net/http2/hpack/huffman.go": "package hpack\nfunc HuffmanDecode() { }\n",
+                               "bar/x.go": input,
+                       },
+               },
+       }.processTest(t, "foo.com", "bar/x.go", nil, nil, want)
+}
+
+func TestFindStdlib(t *testing.T) {
+       tests := []struct {
+               pkg     string
+               symbols []string
+               want    string
+       }{
+               {"http", []string{"Get"}, "net/http"},
+               {"http", []string{"Get", "Post"}, "net/http"},
+               {"http", []string{"Get", "Foo"}, ""},
+               {"bytes", []string{"Buffer"}, "bytes"},
+               {"ioutil", []string{"Discard"}, "io/ioutil"},
+       }
+       for _, tt := range tests {
+               input := "package p\n"
+               for _, sym := range tt.symbols {
+                       input += fmt.Sprintf("var _ = %s.%s\n", tt.pkg, sym)
+               }
+               testConfig{
+                       module: packagestest.Module{
+                               Name:  "foo.com",
+                               Files: fm{"x.go": input},
+                       },
+               }.test(t, func(t *goimportTest) {
+                       buf, err := t.process("foo.com", "x.go", nil, nil)
+                       if err != nil {
+                               t.Fatal(err)
+                       }
+                       if got := string(buf); !strings.Contains(got, tt.want) {
+                               t.Errorf("Process(%q) = %q, wanted it to contain %q", input, buf, tt.want)
+                       }
+               })
+       }
+}
+
+// https://golang.org/issue/31814
+func TestStdlibNotPrefixed(t *testing.T) {
+       const input = `package p
+var _ = bytes.Buffer
+`
+       const want = `package p
+
+import "bytes"
+
+var _ = bytes.Buffer
+`
+       // Force a scan of the stdlib.
+       savedStdlib := stdlib
+       defer func() { stdlib = savedStdlib }()
+       stdlib = map[string][]string{}
+
+       testConfig{
+               module: packagestest.Module{
+                       Name:  "ignored.com",
+                       Files: fm{"x.go": "package x"},
+               },
+       }.test(t, func(t *goimportTest) {
+               // Run in GOROOT/src so that the std module shows up in go list -m all.
+               t.env.WorkingDir = filepath.Join(t.goroot, "src")
+               got, err := t.processNonModule(filepath.Join(t.goroot, "src/x.go"), []byte(input), nil)
+               if err != nil {
+                       t.Fatalf("Process() = %v", err)
+               }
+               if string(got) != want {
+                       t.Errorf("Got:\n%s\nWant:\n%s", got, want)
+               }
+       })
+}
+
+func TestStdlibSelfImports(t *testing.T) {
+       const input = `package ecdsa
+
+var _ = ecdsa.GenerateKey
+`
+
+       testConfig{
+               module: packagestest.Module{
+                       Name:  "ignored.com",
+                       Files: fm{"x.go": "package x"},
+               },
+       }.test(t, func(t *goimportTest) {
+               got, err := t.processNonModule(filepath.Join(t.goroot, "src/crypto/ecdsa/foo.go"), []byte(input), nil)
+               if err != nil {
+                       t.Fatalf("Process() = %v", err)
+               }
+               if string(got) != input {
+                       t.Errorf("Got:\n%s\nWant:\n%s", got, input)
+               }
+       })
+}
+
+type testConfig struct {
+       gopathOnly bool
+       module     packagestest.Module
+       modules    []packagestest.Module
+}
+
+// fm is the type for a packagestest.Module's Files, abbreviated for shorter lines.
+type fm map[string]interface{}
+
+func (c testConfig) test(t *testing.T, fn func(*goimportTest)) {
+       t.Helper()
+
+       if c.module.Name != "" {
+               c.modules = []packagestest.Module{c.module}
+       }
+
+       for _, exporter := range packagestest.All {
+               t.Run(exporter.Name(), func(t *testing.T) {
+                       t.Helper()
+                       if c.gopathOnly && exporter.Name() == "Modules" {
+                               t.Skip("test marked GOPATH-only")
+                       }
+                       exported := packagestest.Export(t, exporter, c.modules)
+                       defer exported.Cleanup()
+
+                       env := map[string]string{}
+                       for _, kv := range exported.Config.Env {
+                               split := strings.SplitN(kv, "=", 2)
+                               env[split[0]] = split[1]
+                       }
+                       it := &goimportTest{
+                               T: t,
+                               env: &ProcessEnv{
+                                       Env:         env,
+                                       WorkingDir:  exported.Config.Dir,
+                                       GocmdRunner: &gocommand.Runner{},
+                               },
+                               exported: exported,
+                       }
+                       if *testDebug {
+                               it.env.Logf = log.Printf
+                       }
+                       // packagestest clears out GOROOT to work around golang/go#32849,
+                       // which isn't relevant here. Fill it back in so we can find the standard library.
+                       it.env.Env["GOROOT"] = build.Default.GOROOT
+                       it.goroot = build.Default.GOROOT
+
+                       fn(it)
+               })
+       }
+}
+
+func (c testConfig) processTest(t *testing.T, module, file string, contents []byte, opts *Options, want string) {
+       t.Helper()
+       c.test(t, func(t *goimportTest) {
+               t.Helper()
+               t.assertProcessEquals(module, file, contents, opts, want)
+       })
+}
+
+type goimportTest struct {
+       *testing.T
+       goroot   string
+       env      *ProcessEnv
+       exported *packagestest.Exported
+}
+
+func (t *goimportTest) process(module, file string, contents []byte, opts *Options) ([]byte, error) {
+       t.Helper()
+       f := t.exported.File(module, file)
+       if f == "" {
+               t.Fatalf("%v not found in exported files (typo in filename?)", file)
+       }
+       return t.processNonModule(f, contents, opts)
+}
+
+func (t *goimportTest) processNonModule(file string, contents []byte, opts *Options) ([]byte, error) {
+       if contents == nil {
+               var err error
+               contents, err = ioutil.ReadFile(file)
+               if err != nil {
+                       return nil, err
+               }
+       }
+       if opts == nil {
+               opts = &Options{Comments: true, TabIndent: true, TabWidth: 8}
+       }
+       // ProcessEnv is not safe for concurrent use. Make a copy.
+       opts.Env = t.env.CopyConfig()
+       return Process(file, contents, opts)
+}
+
+func (t *goimportTest) assertProcessEquals(module, file string, contents []byte, opts *Options, want string) {
+       buf, err := t.process(module, file, contents, opts)
+       if err != nil {
+               t.Fatalf("Process() = %v", err)
+       }
+       if string(buf) != want {
+               t.Errorf("Got:\n%s\nWant:\n%s", buf, want)
+       }
+}
+
+// Tests that added imports are renamed when the import path's base doesn't
+// match its package name.
+func TestRenameWhenPackageNameMismatch(t *testing.T) {
+       const input = `package main
+ const Y = bar.X`
+
+       const want = `package main
+
+import bar "foo.com/foo/bar/baz"
+
+const Y = bar.X
+`
+       testConfig{
+               module: packagestest.Module{
+                       Name: "foo.com",
+                       Files: fm{
+                               "foo/bar/baz/x.go": "package bar \n const X = 1",
+                               "test/t.go":        input,
+                       },
+               },
+       }.processTest(t, "foo.com", "test/t.go", nil, nil, want)
+}
+
+// Tests that an existing import with badly mismatched path/name has its name
+// correctly added. See #28645 and #29041.
+func TestAddNameToMismatchedImport(t *testing.T) {
+       const input = `package main
+
+import (
+"foo.com/a.thing"
+"foo.com/surprise"
+"foo.com/v1"
+"foo.com/other/v2"
+"foo.com/other/v3"
+"foo.com/go-thing"
+"foo.com/go-wrong"
+)
+
+var _ = []interface{}{bar.X, v1.Y, a.A, v2.V2, other.V3, thing.Thing, gow.Wrong}`
+
+       const want = `package main
+
+import (
+       "foo.com/a.thing"
+       "foo.com/go-thing"
+       gow "foo.com/go-wrong"
+       v2 "foo.com/other/v2"
+       "foo.com/other/v3"
+       bar "foo.com/surprise"
+       v1 "foo.com/v1"
+)
+
+var _ = []interface{}{bar.X, v1.Y, a.A, v2.V2, other.V3, thing.Thing, gow.Wrong}
+`
+
+       testConfig{
+               module: packagestest.Module{
+                       Name: "foo.com",
+                       Files: fm{
+                               "a.thing/a.go":  "package a \n const A = 1",
+                               "surprise/x.go": "package bar \n const X = 1",
+                               "v1/x.go":       "package v1 \n const Y = 1",
+                               "other/v2/y.go": "package v2 \n const V2 = 1",
+                               "other/v3/z.go": "package other \n const V3 = 1",
+                               "go-thing/b.go": "package thing \n const Thing = 1",
+                               "go-wrong/b.go": "package gow \n const Wrong = 1",
+                               "test/t.go":     input,
+                       },
+               },
+       }.processTest(t, "foo.com", "test/t.go", nil, nil, want)
+}
+
+// Tests that the LocalPrefix option causes imports
+// to be added into a later group (num=3).
+func TestLocalPrefix(t *testing.T) {
+       tests := []struct {
+               name        string
+               modules     []packagestest.Module
+               localPrefix string
+               src         string
+               want        string
+       }{
+               {
+                       name: "one_local",
+                       modules: []packagestest.Module{
+                               {
+                                       Name: "foo.com",
+                                       Files: fm{
+                                               "bar/bar.go": "package bar \n const X = 1",
+                                       },
+                               },
+                       },
+                       localPrefix: "foo.com/",
+                       src:         "package main \n const Y = bar.X \n const _ = runtime.GOOS",
+                       want: `package main
+
+import (
+       "runtime"
+
+       "foo.com/bar"
+)
+
+const Y = bar.X
+const _ = runtime.GOOS
+`,
+               },
+               {
+                       name: "two_local",
+                       modules: []packagestest.Module{
+                               {
+                                       Name: "foo.com",
+                                       Files: fm{
+                                               "foo/foo.go":     "package foo \n const X = 1",
+                                               "foo/bar/bar.go": "package bar \n const X = 1",
+                                       },
+                               },
+                       },
+                       localPrefix: "foo.com/foo",
+                       src:         "package main \n const Y = bar.X \n const Z = foo.X \n const _ = runtime.GOOS",
+                       want: `package main
+
+import (
+       "runtime"
+
+       "foo.com/foo"
+       "foo.com/foo/bar"
+)
+
+const Y = bar.X
+const Z = foo.X
+const _ = runtime.GOOS
+`,
+               },
+               {
+                       name: "three_prefixes",
+                       modules: []packagestest.Module{
+                               {
+                                       Name:  "example.org/pkg",
+                                       Files: fm{"pkg.go": "package pkg \n const A = 1"},
+                               },
+                               {
+                                       Name:  "foo.com",
+                                       Files: fm{"bar/bar.go": "package bar \n const B = 1"},
+                               },
+                               {
+                                       Name:  "code.org/r/p",
+                                       Files: fm{"expproj/expproj.go": "package expproj \n const C = 1"},
+                               },
+                       },
+                       localPrefix: "example.org/pkg,foo.com/,code.org",
+                       src:         "package main \n const X = pkg.A \n const Y = bar.B \n const Z = expproj.C \n const _ = runtime.GOOS",
+                       want: `package main
+
+import (
+       "runtime"
+
+       "code.org/r/p/expproj"
+       "example.org/pkg"
+       "foo.com/bar"
+)
+
+const X = pkg.A
+const Y = bar.B
+const Z = expproj.C
+const _ = runtime.GOOS
+`,
+               },
+       }
+
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       testConfig{
+                               // The module being processed has to be first so it's the primary module.
+                               modules: append([]packagestest.Module{{
+                                       Name:  "test.com",
+                                       Files: fm{"t.go": tt.src},
+                               }}, tt.modules...),
+                       }.test(t, func(t *goimportTest) {
+                               options := &Options{
+                                       LocalPrefix: tt.localPrefix,
+                                       TabWidth:    8,
+                                       TabIndent:   true,
+                                       Comments:    true,
+                                       Fragment:    true,
+                               }
+                               t.assertProcessEquals("test.com", "t.go", nil, options, tt.want)
+                       })
+               })
+       }
+}
+
+// Tests that "package documentation" files are ignored.
+func TestIgnoreDocumentationPackage(t *testing.T) {
+       const input = `package x
+
+const Y = foo.X
+`
+       const want = `package x
+
+import "foo.com/foo"
+
+const Y = foo.X
+`
+
+       testConfig{
+               module: packagestest.Module{
+                       Name: "foo.com",
+                       Files: fm{
+                               "foo/foo.go": "package foo\nconst X = 1\n",
+                               "foo/doc.go": "package documentation \n // just to confuse things\n",
+                               "x/x.go":     input,
+                       },
+               },
+       }.processTest(t, "foo.com", "x/x.go", nil, nil, want)
+}
+
+// Tests importPathToNameGoPathParse and in particular that it stops
+// after finding the first non-documentation package name, not
+// reporting an error on inconsistent package names (since it should
+// never make it that far).
+func TestImportPathToNameGoPathParse(t *testing.T) {
+       testConfig{
+               module: packagestest.Module{
+                       Name: "example.net/pkg",
+                       Files: fm{
+                               "doc.go": "package documentation\n", // ignored
+                               "gen.go": "package main\n",          // also ignored
+                               "pkg.go": "package the_pkg_name_to_find\n  and this syntax error is ignored because of parser.PackageClauseOnly",
+                               "z.go":   "package inconsistent\n", // inconsistent but ignored
+                       },
+               },
+       }.test(t, func(t *goimportTest) {
+               if strings.Contains(t.Name(), "GoPackages") {
+                       t.Skip("go/packages does not ignore package main")
+               }
+               r, err := t.env.GetResolver()
+               if err != nil {
+                       t.Fatal(err)
+               }
+               srcDir := filepath.Dir(t.exported.File("example.net/pkg", "z.go"))
+               names, err := r.loadPackageNames([]string{"example.net/pkg"}, srcDir)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               const want = "the_pkg_name_to_find"
+               if got := names["example.net/pkg"]; got != want {
+                       t.Errorf("loadPackageNames(..) = %q; want %q", got, want)
+               }
+       })
+}
+
+func TestIgnoreConfiguration(t *testing.T) {
+       const input = `package x
+
+const _ = pkg.X
+`
+       const want = `package x
+
+import "foo.com/otherwise-longer-so-worse-example/foo/pkg"
+
+const _ = pkg.X
+`
+
+       testConfig{
+               gopathOnly: true,
+               module: packagestest.Module{
+                       Name: "foo.com",
+                       Files: fm{
+                               "../.goimportsignore":                              "# comment line\n\n foo.com/example", // tests comment, blank line, whitespace trimming
+                               "example/pkg/pkg.go":                               "package pkg\nconst X = 1",
+                               "otherwise-longer-so-worse-example/foo/pkg/pkg.go": "package pkg\nconst X = 1",
+                               "x/x.go": input,
+                       },
+               },
+       }.processTest(t, "foo.com", "x/x.go", nil, nil, want)
+}
+
+// Skip "node_modules" directory.
+func TestSkipNodeModules(t *testing.T) {
+       const input = `package x
+
+const _ = pkg.X
+`
+       const want = `package x
+
+import "foo.com/otherwise-longer/not_modules/pkg"
+
+const _ = pkg.X
+`
+
+       testConfig{
+               gopathOnly: true,
+               module: packagestest.Module{
+                       Name: "foo.com",
+                       Files: fm{
+                               "example/node_modules/pkg/a.go":         "package pkg\nconst X = 1",
+                               "otherwise-longer/not_modules/pkg/a.go": "package pkg\nconst X = 1",
+                               "x/x.go":                                input,
+                       },
+               },
+       }.processTest(t, "foo.com", "x/x.go", nil, nil, want)
+}
+
+// Tests that package global variables with the same name and function name as
+// a function in a separate package do not result in an import which masks
+// the global variable
+func TestGlobalImports(t *testing.T) {
+       const usesGlobal = `package pkg
+
+func doSomething() {
+       t := time.Now()
+}
+`
+
+       const declaresGlobal = `package pkg
+
+type Time struct{}
+
+func (t Time) Now() Time {
+       return Time{}
+}
+
+var time Time
+`
+
+       testConfig{
+               module: packagestest.Module{
+                       Name: "foo.com",
+                       Files: fm{
+                               "pkg/uses.go":   usesGlobal,
+                               "pkg/global.go": declaresGlobal,
+                       },
+               },
+       }.processTest(t, "foo.com", "pkg/uses.go", nil, nil, usesGlobal)
+}
+
+// Some people put multiple packages' files in the same directory. Globals
+// declared in other packages should be ignored.
+func TestGlobalImports_DifferentPackage(t *testing.T) {
+       const declaresGlobal = `package main
+var fmt int
+`
+       const input = `package pkg
+var _ = fmt.Printf
+`
+       const want = `package pkg
+
+import "fmt"
+
+var _ = fmt.Printf
+`
+
+       testConfig{
+               module: packagestest.Module{
+                       Name: "foo.com",
+                       Files: fm{
+                               "pkg/main.go": declaresGlobal,
+                               "pkg/uses.go": input,
+                       },
+               },
+       }.processTest(t, "foo.com", "pkg/uses.go", nil, nil, want)
+}
+
+func TestGlobalImports_MultipleMains(t *testing.T) {
+       const declaresGlobal = `package main
+var fmt int
+`
+       const input = `package main
+import "fmt"
+var _, _ = fmt.Printf, bytes.Equal
+`
+       const want = `package main
+
+import (
+       "bytes"
+       "fmt"
+)
+
+var _, _ = fmt.Printf, bytes.Equal
+`
+
+       testConfig{
+               module: packagestest.Module{
+                       Name: "foo.com",
+                       Files: fm{
+                               "pkg/main.go": declaresGlobal,
+                               "pkg/uses.go": input,
+                       },
+               },
+       }.processTest(t, "foo.com", "pkg/uses.go", nil, nil, want)
+}
+
+// Tests that sibling files - other files in the same package - can provide an
+// import that may not be the default one otherwise.
+func TestSiblingImports(t *testing.T) {
+
+       // provide is the sibling file that provides the desired import.
+       const provide = `package siblingimporttest
+
+import "local/log"
+import "my/bytes"
+import renamed "fmt"
+
+func LogSomething() {
+       log.Print("Something")
+       bytes.SomeFunc()
+       renamed.Println("Something")
+}
+`
+
+       // need is the file being tested that needs the import.
+       const need = `package siblingimporttest
+
+var _ = bytes.Buffer{}
+
+func LogSomethingElse() {
+       log.Print("Something else")
+       renamed.Println("Yet another")
+}
+`
+
+       // want is the expected result file
+       const want = `package siblingimporttest
+
+import (
+       "bytes"
+       renamed "fmt"
+       "local/log"
+)
+
+var _ = bytes.Buffer{}
+
+func LogSomethingElse() {
+       log.Print("Something else")
+       renamed.Println("Yet another")
+}
+`
+
+       testConfig{
+               module: packagestest.Module{
+                       Name: "foo.com",
+                       Files: fm{
+                               "p/needs_import.go":    need,
+                               "p/provides_import.go": provide,
+                       },
+               },
+       }.processTest(t, "foo.com", "p/needs_import.go", nil, nil, want)
+}
+
+// Tests #29180: a sibling import of the right package with the wrong name is used.
+func TestSiblingImport_Misnamed(t *testing.T) {
+       const sibling = `package main
+import renamed "fmt"
+var _ = renamed.Printf
+`
+       const input = `package pkg
+var _ = fmt.Printf
+`
+       const want = `package pkg
+
+import "fmt"
+
+var _ = fmt.Printf
+`
+
+       testConfig{
+               module: packagestest.Module{
+                       Name: "foo.com",
+                       Files: fm{
+                               "pkg/main.go": sibling,
+                               "pkg/uses.go": input,
+                       },
+               },
+       }.processTest(t, "foo.com", "pkg/uses.go", nil, nil, want)
+
+}
+
+// Tests that an input file's own package is ignored.
+func TestIgnoreOwnPackage(t *testing.T) {
+       const input = `package pkg
+
+const _ = pkg.X
+`
+       const want = `package pkg
+
+const _ = pkg.X
+`
+
+       testConfig{
+               module: packagestest.Module{
+                       Name: "foo.com",
+                       Files: fm{
+                               "pkg/a.go": "package pkg\nconst X = 1",
+                               "pkg/b.go": input,
+                       },
+               },
+       }.processTest(t, "foo.com", "pkg/b.go", nil, nil, want)
+}
+
+func TestExternalTestImportsPackageUnderTest(t *testing.T) {
+       const provide = `package pkg
+func DoIt(){}
+`
+       const input = `package pkg_test
+
+var _ = pkg.DoIt`
+
+       const want = `package pkg_test
+
+import "foo.com/pkg"
+
+var _ = pkg.DoIt
+`
+
+       testConfig{
+               module: packagestest.Module{
+                       Name: "foo.com",
+                       Files: fm{
+                               "pkg/provide.go": provide,
+                               "pkg/x_test.go":  input,
+                       },
+               },
+       }.processTest(t, "foo.com", "pkg/x_test.go", nil, nil, want)
+}
+
+func TestPkgIsCandidate(t *testing.T) {
+       tests := []struct {
+               name     string
+               filename string
+               pkgIdent string
+               pkg      *pkg
+               want     bool
+       }{
+               {
+                       name:     "normal_match",
+                       filename: "/gopath/src/my/pkg/pkg.go",
+                       pkgIdent: "client",
+                       pkg: &pkg{
+                               dir:             "/gopath/src/client",
+                               importPathShort: "client",
+                       },
+                       want: true,
+               },
+               {
+                       name:     "no_match",
+                       filename: "/gopath/src/my/pkg/pkg.go",
+                       pkgIdent: "zzz",
+                       pkg: &pkg{
+                               dir:             "/gopath/src/client",
+                               importPathShort: "client",
+                       },
+                       want: false,
+               },
+               {
+                       name:     "match_too_early",
+                       filename: "/gopath/src/my/pkg/pkg.go",
+                       pkgIdent: "client",
+                       pkg: &pkg{
+                               dir:             "/gopath/src/client/foo/foo/foo",
+                               importPathShort: "client/foo/foo",
+                       },
+                       want: false,
+               },
+               {
+                       name:     "substring_match",
+                       filename: "/gopath/src/my/pkg/pkg.go",
+                       pkgIdent: "client",
+                       pkg: &pkg{
+                               dir:             "/gopath/src/foo/go-client",
+                               importPathShort: "foo/go-client",
+                       },
+                       want: true,
+               },
+               {
+                       name:     "hidden_internal",
+                       filename: "/gopath/src/my/pkg/pkg.go",
+                       pkgIdent: "client",
+                       pkg: &pkg{
+                               dir:             "/gopath/src/foo/internal/client",
+                               importPathShort: "foo/internal/client",
+                       },
+                       want: false,
+               },
+               {
+                       name:     "visible_internal",
+                       filename: "/gopath/src/foo/bar.go",
+                       pkgIdent: "client",
+                       pkg: &pkg{
+                               dir:             "/gopath/src/foo/internal/client",
+                               importPathShort: "foo/internal/client",
+                       },
+                       want: true,
+               },
+               {
+                       name:     "invisible_vendor",
+                       filename: "/gopath/src/foo/bar.go",
+                       pkgIdent: "client",
+                       pkg: &pkg{
+                               dir:             "/gopath/src/other/vendor/client",
+                               importPathShort: "client",
+                       },
+                       want: false,
+               },
+               {
+                       name:     "visible_vendor",
+                       filename: "/gopath/src/foo/bar.go",
+                       pkgIdent: "client",
+                       pkg: &pkg{
+                               dir:             "/gopath/src/foo/vendor/client",
+                               importPathShort: "client",
+                       },
+                       want: true,
+               },
+               {
+                       name:     "match_with_hyphens",
+                       filename: "/gopath/src/foo/bar.go",
+                       pkgIdent: "socketio",
+                       pkg: &pkg{
+                               dir:             "/gopath/src/foo/socket-io",
+                               importPathShort: "foo/socket-io",
+                       },
+                       want: true,
+               },
+               {
+                       name:     "match_with_mixed_case",
+                       filename: "/gopath/src/foo/bar.go",
+                       pkgIdent: "fooprod",
+                       pkg: &pkg{
+                               dir:             "/gopath/src/foo/FooPROD",
+                               importPathShort: "foo/FooPROD",
+                       },
+                       want: true,
+               },
+               {
+                       name:     "matches_with_hyphen_and_caps",
+                       filename: "/gopath/src/foo/bar.go",
+                       pkgIdent: "fooprod",
+                       pkg: &pkg{
+                               dir:             "/gopath/src/foo/Foo-PROD",
+                               importPathShort: "foo/Foo-PROD",
+                       },
+                       want: true,
+               },
+       }
+       for i, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       refs := references{tt.pkgIdent: nil}
+                       got := pkgIsCandidate(tt.filename, refs, tt.pkg)
+                       if got != tt.want {
+                               t.Errorf("test %d. pkgIsCandidate(%q, %q, %+v) = %v; want %v",
+                                       i, tt.filename, tt.pkgIdent, *tt.pkg, got, tt.want)
+                       }
+               })
+       }
+}
+
+// Issue 20941: this used to panic on Windows.
+func TestProcessStdin(t *testing.T) {
+       testConfig{
+               module: packagestest.Module{
+                       Name: "foo.com",
+               },
+       }.test(t, func(t *goimportTest) {
+               got, err := t.processNonModule("<standard input>", []byte("package main\nfunc main() {\n\tfmt.Println(123)\n}\n"), nil)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               if !strings.Contains(string(got), `"fmt"`) {
+                       t.Errorf("expected fmt import; got: %s", got)
+               }
+       })
+}
+
+// Tests LocalPackagePromotion when there is a local package that matches, it
+// should be the closest match.
+// https://golang.org/issues/17557
+func TestLocalPackagePromotion(t *testing.T) {
+       const input = `package main
+var c = &config.SystemConfig{}
+`
+       const want = `package main
+
+import "mycompany.net/tool/config"
+
+var c = &config.SystemConfig{}
+`
+
+       testConfig{
+               modules: []packagestest.Module{
+                       {
+                               Name:  "config.net/config",
+                               Files: fm{"config.go": "package config\n type SystemConfig struct {}"}, // Will match but should not be first choice
+                       },
+                       {
+                               Name:  "mycompany.net/config",
+                               Files: fm{"config.go": "package config\n type SystemConfig struct {}"}, // Will match but should not be first choice
+                       },
+                       {
+                               Name: "mycompany.net/tool",
+                               Files: fm{
+                                       "config/config.go": "package config\n type SystemConfig struct {}", // Local package should be promoted over shorter package
+                                       "main.go":          input,
+                               },
+                       },
+               },
+       }.processTest(t, "mycompany.net/tool", "main.go", nil, nil, want)
+}
+
+// Tests FindImportInLocalGoFiles looks at the import lines for other Go files in the
+// local directory, since the user is likely to import the same packages in the current
+// Go file.  If an import is found that satisfies the need, it should be used over the
+// standard library.
+// https://golang.org/issues/17557
+func TestFindImportInLocalGoFiles(t *testing.T) {
+       const input = `package main
+ var _ = &bytes.Buffer{}`
+
+       const want = `package main
+
+import "bytes.net/bytes"
+
+var _ = &bytes.Buffer{}
+`
+       testConfig{
+               modules: []packagestest.Module{
+                       {
+                               Name: "mycompany.net/tool",
+                               Files: fm{
+                                       "io.go":   "package main\n import \"bytes.net/bytes\"\n var _ = &bytes.Buffer{}", // Contains package import that will cause stdlib to be ignored
+                                       "main.go": input,
+                               },
+                       },
+                       {
+                               Name:  "bytes.net/bytes",
+                               Files: fm{"bytes.go": "package bytes\n type Buffer struct {}"}, // Should be selected over standard library
+                       },
+               },
+       }.processTest(t, "mycompany.net/tool", "main.go", nil, nil, want)
+}
+
+func TestInMemoryFile(t *testing.T) {
+       const input = `package main
+ var _ = &bytes.Buffer{}`
+
+       const want = `package main
+
+import "bytes"
+
+var _ = &bytes.Buffer{}
+`
+       testConfig{
+               module: packagestest.Module{
+                       Name:  "foo.com",
+                       Files: fm{"x.go": "package x\n"},
+               },
+       }.processTest(t, "foo.com", "x.go", []byte(input), nil, want)
+}
+
+func TestImportNoGoFiles(t *testing.T) {
+       const input = `package main
+ var _ = &bytes.Buffer{}`
+
+       const want = `package main
+
+import "bytes"
+
+var _ = &bytes.Buffer{}
+`
+       testConfig{
+               module: packagestest.Module{
+                       Name: "mycompany.net",
+               },
+       }.test(t, func(t *goimportTest) {
+               buf, err := t.processNonModule("mycompany.net/tool/main.go", []byte(input), nil)
+               if err != nil {
+                       t.Fatalf("Process() = %v", err)
+               }
+               if string(buf) != want {
+                       t.Errorf("Got:\n%s\nWant:\n%s", buf, want)
+               }
+       })
+
+}
+
+// Ensures a token as large as 500000 bytes can be handled
+// https://golang.org/issues/18201
+func TestProcessLargeToken(t *testing.T) {
+       largeString := strings.Repeat("x", 500000)
+
+       input := `package testimports
+
+import (
+       "bytes"
+)
+
+const s = fmt.Sprintf("%s", "` + largeString + `")
+var _ = bytes.Buffer{}
+
+// end
+`
+
+       want := `package testimports
+
+import (
+       "bytes"
+       "fmt"
+)
+
+const s = fmt.Sprintf("%s", "` + largeString + `")
+
+var _ = bytes.Buffer{}
+
+// end
+`
+
+       testConfig{
+               module: packagestest.Module{
+                       Name:  "foo.com",
+                       Files: fm{"foo.go": input},
+               },
+       }.processTest(t, "foo.com", "foo.go", nil, nil, want)
+}
+
+// Tests that an external test package will import the package under test if it
+// also uses symbols exported only in test files.
+// https://golang.org/issues/29979
+func TestExternalTest(t *testing.T) {
+       const input = `package a_test
+func TestX() {
+       a.X()
+       a.Y()
+}
+`
+       const want = `package a_test
+
+import "foo.com/a"
+
+func TestX() {
+       a.X()
+       a.Y()
+}
+`
+
+       testConfig{
+               modules: []packagestest.Module{
+                       {
+                               Name: "foo.com/a",
+                               Files: fm{
+                                       "a.go":           "package a\n func X() {}",
+                                       "export_test.go": "package a\n func Y() {}",
+                                       "a_test.go":      input,
+                               },
+                       },
+               },
+       }.processTest(t, "foo.com/a", "a_test.go", nil, nil, want)
+}
+
+// TestGetCandidates tests that get packages finds packages
+// with correct priorities.
+func TestGetCandidates(t *testing.T) {
+       type res struct {
+               relevance  float64
+               name, path string
+       }
+       want := []res{
+               {0, "bytes", "bytes"},
+               {0, "http", "net/http"},
+               {0, "rand", "crypto/rand"},
+               {0, "bar", "bar.com/bar"},
+               {0, "foo", "foo.com/foo"},
+       }
+
+       testConfig{
+               modules: []packagestest.Module{
+                       {
+                               Name:  "bar.com",
+                               Files: fm{"bar/bar.go": "package bar\n"},
+                       },
+                       {
+                               Name:  "foo.com",
+                               Files: fm{"foo/foo.go": "package foo\n"},
+                       },
+               },
+       }.test(t, func(t *goimportTest) {
+               var mu sync.Mutex
+               var got []res
+               add := func(c ImportFix) {
+                       mu.Lock()
+                       defer mu.Unlock()
+                       for _, w := range want {
+                               if c.StmtInfo.ImportPath == w.path {
+                                       got = append(got, res{c.Relevance, c.IdentName, c.StmtInfo.ImportPath})
+                               }
+                       }
+               }
+               if err := GetAllCandidates(context.Background(), add, "", "x.go", "x", t.env); err != nil {
+                       t.Fatalf("GetAllCandidates() = %v", err)
+               }
+               // Sort, then clear out relevance so it doesn't mess up the DeepEqual.
+               sort.Slice(got, func(i, j int) bool {
+                       ri, rj := got[i], got[j]
+                       if ri.relevance != rj.relevance {
+                               return ri.relevance > rj.relevance // Highest first.
+                       }
+                       return ri.name < rj.name
+               })
+               for i := range got {
+                       got[i].relevance = 0
+               }
+               if !reflect.DeepEqual(want, got) {
+                       t.Errorf("wanted results in order %v, got %v", want, got)
+               }
+       })
+}
+
+func TestGetImportPaths(t *testing.T) {
+       type res struct {
+               relevance  float64
+               name, path string
+       }
+       want := []res{
+               {0, "http", "net/http"},
+               {0, "net", "net"},
+               {0, "neta", "neta.com/neta"},
+       }
+
+       testConfig{
+               modules: []packagestest.Module{
+                       {
+                               Name:  "neta.com",
+                               Files: fm{"neta/neta.go": "package neta\n"},
+                       },
+               },
+       }.test(t, func(t *goimportTest) {
+               var mu sync.Mutex
+               var got []res
+               add := func(c ImportFix) {
+                       mu.Lock()
+                       defer mu.Unlock()
+                       for _, w := range want {
+                               if c.StmtInfo.ImportPath == w.path {
+                                       got = append(got, res{c.Relevance, c.IdentName, c.StmtInfo.ImportPath})
+                               }
+                       }
+               }
+               if err := GetImportPaths(context.Background(), add, "ne", "x.go", "x", t.env); err != nil {
+                       t.Fatalf("GetImportPaths() = %v", err)
+               }
+               // Sort, then clear out relevance so it doesn't mess up the DeepEqual.
+               sort.Slice(got, func(i, j int) bool {
+                       ri, rj := got[i], got[j]
+                       if ri.relevance != rj.relevance {
+                               return ri.relevance > rj.relevance // Highest first.
+                       }
+                       return ri.name < rj.name
+               })
+               for i := range got {
+                       got[i].relevance = 0
+               }
+               if !reflect.DeepEqual(want, got) {
+                       t.Errorf("wanted results in order %v, got %v", want, got)
+               }
+       })
+}
+
+func TestGetPackageCompletions(t *testing.T) {
+       type res struct {
+               relevance          float64
+               name, path, symbol string
+       }
+       want := []res{
+               {0, "rand", "math/rand", "Seed"},
+               {0, "rand", "bar.com/rand", "Bar"},
+       }
+
+       testConfig{
+               modules: []packagestest.Module{
+                       {
+                               Name:  "bar.com",
+                               Files: fm{"rand/bar.go": "package rand\nvar Bar int\n"},
+                       },
+               },
+       }.test(t, func(t *goimportTest) {
+               var mu sync.Mutex
+               var got []res
+               add := func(c PackageExport) {
+                       mu.Lock()
+                       defer mu.Unlock()
+                       for _, csym := range c.Exports {
+                               for _, w := range want {
+                                       if c.Fix.StmtInfo.ImportPath == w.path && csym == w.symbol {
+                                               got = append(got, res{c.Fix.Relevance, c.Fix.IdentName, c.Fix.StmtInfo.ImportPath, csym})
+                                       }
+                               }
+                       }
+               }
+               if err := GetPackageExports(context.Background(), add, "rand", "x.go", "x", t.env); err != nil {
+                       t.Fatalf("getPackageCompletions() = %v", err)
+               }
+               // Sort, then clear out relevance so it doesn't mess up the DeepEqual.
+               sort.Slice(got, func(i, j int) bool {
+                       ri, rj := got[i], got[j]
+                       if ri.relevance != rj.relevance {
+                               return ri.relevance > rj.relevance // Highest first.
+                       }
+                       return ri.name < rj.name
+               })
+               for i := range got {
+                       got[i].relevance = 0
+               }
+               if !reflect.DeepEqual(want, got) {
+                       t.Errorf("wanted results in order %v, got %v", want, got)
+               }
+       })
+}
+
+// Tests #34895: process should not panic on concurrent calls.
+func TestConcurrentProcess(t *testing.T) {
+       testConfig{
+               module: packagestest.Module{
+                       Name: "foo.com",
+                       Files: fm{
+                               "p/first.go": `package foo
+
+func _() {
+       fmt.Println()
+}
+`,
+                               "p/second.go": `package foo
+
+import "fmt"
+
+func _() {
+       fmt.Println()
+       imports.Bar() // not imported.
+}
+`,
+                       },
+               },
+       }.test(t, func(t *goimportTest) {
+               var (
+                       n  = 10
+                       wg sync.WaitGroup
+               )
+               wg.Add(n)
+               for i := 0; i < n; i++ {
+                       go func() {
+                               defer wg.Done()
+                               _, err := t.process("foo.com", "p/first.go", nil, nil)
+                               if err != nil {
+                                       t.Error(err)
+                               }
+                       }()
+               }
+               wg.Wait()
+       })
+}
+
+func TestNonlocalDot(t *testing.T) {
+       const input = `package main
+import (
+       "fmt"
+)
+var _, _ = fmt.Sprintf, dot.Dot
+`
+       const want = `package main
+
+import (
+       "fmt"
+       "noninternet/dot.v1/dot"
+)
+
+var _, _ = fmt.Sprintf, dot.Dot
+`
+       testConfig{
+               modules: []packagestest.Module{
+                       {
+                               Name:  "golang.org/fake",
+                               Files: fm{"x.go": input},
+                       },
+                       {
+                               Name: "noninternet/dot.v1",
+                               Files: fm{
+                                       "dot/dot.go": "package dot\nfunc Dot(){}\n",
+                               },
+                       },
+               },
+               gopathOnly: true, // our modules testing setup doesn't allow modules without dots.
+       }.processTest(t, "golang.org/fake", "x.go", nil, nil, want)
+}