mirror of
https://github.com/golang/go
synced 2024-11-18 14:14:46 -07:00
6e57528ade
Don't panic when reading from stdin on Windows. This is a regression from https://golang.org/cl/43454 Also fix some weird behavior with stdin processing I noticed during reviewing the code: don't allow the -w (write) flag, and adust the filename shown with the -d (diff) flag. Fixes golang/go#20941 Change-Id: I73d0a1dc74c919238a3bb72823585bbf1b7daba1 Reviewed-on: https://go-review.googlesource.com/47810 Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Brad Jones <rbjones@google.com> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
1817 lines
35 KiB
Go
1817 lines
35 KiB
Go
// 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 (
|
|
"bytes"
|
|
"flag"
|
|
"go/build"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
)
|
|
|
|
var only = flag.String("only", "", "If non-empty, the fix test to run")
|
|
|
|
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"
|
|
|
|
"appengine"
|
|
)
|
|
func bar() {
|
|
var b bytes.Buffer
|
|
_ = appengine.IsDevServer
|
|
fmt.Println(b.String())
|
|
}
|
|
`,
|
|
out: `package foo
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
|
|
"appengine"
|
|
)
|
|
|
|
func bar() {
|
|
var b bytes.Buffer
|
|
_ = appengine.IsDevServer
|
|
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"
|
|
|
|
"appengine"
|
|
)
|
|
func bar() {
|
|
_ = math.NaN
|
|
_ = fmt.Sprintf
|
|
_ = appengine.IsDevServer
|
|
}
|
|
`,
|
|
out: `package foo
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
|
|
"appengine"
|
|
)
|
|
|
|
func bar() {
|
|
_ = math.NaN
|
|
_ = fmt.Sprintf
|
|
_ = appengine.IsDevServer
|
|
}
|
|
`,
|
|
},
|
|
|
|
// 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
|
|
}
|
|
`,
|
|
},
|
|
|
|
// 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
|
|
_, _ = appengine.FooSomething, user.Current
|
|
}
|
|
`,
|
|
out: `package foo
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
|
|
"appengine"
|
|
"appengine/user"
|
|
)
|
|
|
|
func foo() {
|
|
_, _ = os.Args, fmt.Println
|
|
_, _ = appengine.FooSomething, user.Current
|
|
}
|
|
`,
|
|
},
|
|
|
|
// 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.Foo
|
|
}
|
|
`,
|
|
out: `package foo
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
|
|
"code.google.com/p/snappy-go/snappy"
|
|
)
|
|
|
|
func f() {
|
|
_ = net.Dial
|
|
_ = fmt.Printf
|
|
_ = snappy.Foo
|
|
}
|
|
`,
|
|
},
|
|
|
|
// Blank line between standard library and third-party stuff.
|
|
{
|
|
name: "blank_line_separating_std_and_third_party",
|
|
in: `package foo
|
|
|
|
import (
|
|
"code.google.com/p/snappy-go/snappy"
|
|
"fmt"
|
|
"net"
|
|
)
|
|
|
|
func f() {
|
|
_ = net.Dial
|
|
_ = fmt.Printf
|
|
_ = snappy.Foo
|
|
}
|
|
`,
|
|
out: `package foo
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
|
|
"code.google.com/p/snappy-go/snappy"
|
|
)
|
|
|
|
func f() {
|
|
_ = net.Dial
|
|
_ = fmt.Printf
|
|
_ = snappy.Foo
|
|
}
|
|
`,
|
|
},
|
|
|
|
// golang.org/issue/6884
|
|
{
|
|
name: "issue 6884",
|
|
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: "issue 7132",
|
|
in: `package main
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"gu"
|
|
"github.com/foo/bar"
|
|
)
|
|
|
|
var (
|
|
a = bar.a
|
|
b = gu.a
|
|
c = fmt.Printf
|
|
)
|
|
`,
|
|
out: `package main
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"gu"
|
|
|
|
"github.com/foo/bar"
|
|
)
|
|
|
|
var (
|
|
a = bar.a
|
|
b = gu.a
|
|
c = fmt.Printf
|
|
)
|
|
`,
|
|
},
|
|
|
|
{
|
|
name: "renamed package",
|
|
in: `package main
|
|
|
|
var _ = str.HasPrefix
|
|
`,
|
|
out: `package main
|
|
|
|
import str "strings"
|
|
|
|
var _ = str.HasPrefix
|
|
`,
|
|
},
|
|
|
|
{
|
|
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: "issue 7679",
|
|
in: `package main
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/foo/bar"
|
|
"github.com/foo/qux"
|
|
)
|
|
|
|
func main() {
|
|
var _ = fmt.Println
|
|
//var _ = bar.A
|
|
var _ = qux.B
|
|
}
|
|
`,
|
|
out: `package main
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/foo/qux"
|
|
)
|
|
|
|
func main() {
|
|
var _ = fmt.Println
|
|
//var _ = bar.A
|
|
var _ = qux.B
|
|
}
|
|
`,
|
|
},
|
|
|
|
// Blank line can be added before all types of import declarations.
|
|
// golang.org/issue/7866
|
|
{
|
|
name: "issue 7866",
|
|
in: `package main
|
|
|
|
import (
|
|
"fmt"
|
|
renamed_bar "github.com/foo/bar"
|
|
|
|
. "github.com/foo/baz"
|
|
"io"
|
|
|
|
_ "github.com/foo/qux"
|
|
"strings"
|
|
)
|
|
|
|
func main() {
|
|
_, _, _, _, _ = fmt.Errorf, io.Copy, strings.Contains, renamed_bar.A, B
|
|
}
|
|
`,
|
|
out: `package main
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
renamed_bar "github.com/foo/bar"
|
|
|
|
"io"
|
|
|
|
. "github.com/foo/baz"
|
|
|
|
"strings"
|
|
|
|
_ "github.com/foo/qux"
|
|
)
|
|
|
|
func main() {
|
|
_, _, _, _, _ = fmt.Errorf, io.Copy, strings.Contains, renamed_bar.A, B
|
|
}
|
|
`,
|
|
},
|
|
|
|
// Non-idempotent comment formatting
|
|
// golang.org/issue/8035
|
|
{
|
|
name: "issue 8035",
|
|
in: `package main
|
|
|
|
import (
|
|
"fmt" // A
|
|
"go/ast" // B
|
|
_ "launchpad.net/gocheck" // C
|
|
)
|
|
|
|
func main() { _, _ = fmt.Print, ast.Walk }
|
|
`,
|
|
out: `package main
|
|
|
|
import (
|
|
"fmt" // A
|
|
"go/ast" // B
|
|
|
|
_ "launchpad.net/gocheck" // C
|
|
)
|
|
|
|
func main() { _, _ = fmt.Print, ast.Walk }
|
|
`,
|
|
},
|
|
|
|
// Failure to delete all duplicate imports
|
|
// golang.org/issue/8459
|
|
{
|
|
name: "issue 8459",
|
|
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: "issue 9961",
|
|
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: "issue 8149",
|
|
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: "issue 8149",
|
|
in: `package main
|
|
|
|
import (
|
|
"fmt"
|
|
x "fmt"
|
|
)
|
|
|
|
func main() { fmt.Println() }
|
|
`,
|
|
out: `package main
|
|
|
|
import (
|
|
"fmt"
|
|
)
|
|
|
|
func main() { fmt.Println() }
|
|
`,
|
|
},
|
|
|
|
// FormatOnly
|
|
{
|
|
name: "format only",
|
|
formatOnly: true,
|
|
in: `package main
|
|
|
|
import (
|
|
"fmt"
|
|
"golang.org/x/foo"
|
|
)
|
|
|
|
func main() {}
|
|
`,
|
|
out: `package main
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"golang.org/x/foo"
|
|
)
|
|
|
|
func main() {}
|
|
`,
|
|
},
|
|
|
|
{
|
|
name: "do not make grouped imports non-grouped",
|
|
in: `package p
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
)
|
|
|
|
var _ = fmt.Sprintf
|
|
`,
|
|
out: `package p
|
|
|
|
import (
|
|
"fmt"
|
|
)
|
|
|
|
var _ = fmt.Sprintf
|
|
`,
|
|
},
|
|
}
|
|
|
|
func TestFixImports(t *testing.T) {
|
|
simplePkgs := map[string]string{
|
|
"appengine": "appengine",
|
|
"bytes": "bytes",
|
|
"fmt": "fmt",
|
|
"math": "math",
|
|
"os": "os",
|
|
"p": "rsc.io/p",
|
|
"regexp": "regexp",
|
|
"snappy": "code.google.com/p/snappy-go/snappy",
|
|
"str": "strings",
|
|
"user": "appengine/user",
|
|
"zip": "archive/zip",
|
|
}
|
|
old := findImport
|
|
defer func() {
|
|
findImport = old
|
|
}()
|
|
findImport = func(pkgName string, symbols map[string]bool, filename string) (string, bool, error) {
|
|
return simplePkgs[pkgName], pkgName == "str", nil
|
|
}
|
|
|
|
options := &Options{
|
|
TabWidth: 8,
|
|
TabIndent: true,
|
|
Comments: true,
|
|
Fragment: true,
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
options.FormatOnly = tt.formatOnly
|
|
if *only != "" && tt.name != *only {
|
|
continue
|
|
}
|
|
buf, err := Process(tt.name+".go", []byte(tt.in), options)
|
|
if err != nil {
|
|
t.Errorf("error on %q: %v", tt.name, err)
|
|
continue
|
|
}
|
|
if got := string(buf); got != tt.out {
|
|
t.Errorf("results diff on %q\nGOT:\n%s\nWANT:\n%s\n", tt.name, got, 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)
|
|
}
|
|
|
|
newGoPath, err := ioutil.TempDir("", "symlinktest")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer os.RemoveAll(newGoPath)
|
|
|
|
// Create:
|
|
// $GOPATH/target/
|
|
// $GOPATH/target/f.go // package mypkg\nvar Foo = 123\n
|
|
// $GOPATH/src/x/
|
|
// $GOPATH/src/x/mypkg => $GOPATH/target // symlink
|
|
// $GOPATH/src/x/apkg => $GOPATH/src/x // symlink loop
|
|
// Test:
|
|
// $GOPATH/src/myotherpkg/toformat.go referencing mypkg.Foo
|
|
|
|
targetPath := newGoPath + "/target"
|
|
if err := os.MkdirAll(targetPath, 0755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := ioutil.WriteFile(targetPath+"/f.go", []byte("package mypkg\nvar Foo = 123\n"), 0666); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
symlinkPath := newGoPath + "/src/x/mypkg"
|
|
if err := os.MkdirAll(filepath.Dir(symlinkPath), 0755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.Symlink(targetPath, symlinkPath); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Add a symlink loop.
|
|
if err := os.Symlink(newGoPath+"/src/x", newGoPath+"/src/x/apkg"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
withEmptyGoPath(func() {
|
|
build.Default.GOPATH = newGoPath
|
|
|
|
input := `package p
|
|
|
|
var (
|
|
_ = fmt.Print
|
|
_ = mypkg.Foo
|
|
)
|
|
`
|
|
output := `package p
|
|
|
|
import (
|
|
"fmt"
|
|
"x/mypkg"
|
|
)
|
|
|
|
var (
|
|
_ = fmt.Print
|
|
_ = mypkg.Foo
|
|
)
|
|
`
|
|
buf, err := Process(newGoPath+"/src/myotherpkg/toformat.go", []byte(input), &Options{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := string(buf); got != output {
|
|
t.Fatalf("results differ\nGOT:\n%s\nWANT:\n%s\n", got, output)
|
|
}
|
|
})
|
|
|
|
// Add a .goimportsignore and ensure it is respected.
|
|
if err := ioutil.WriteFile(newGoPath+"/src/.goimportsignore", []byte("x/mypkg\n"), 0666); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
withEmptyGoPath(func() {
|
|
build.Default.GOPATH = newGoPath
|
|
|
|
input := `package p
|
|
|
|
var (
|
|
_ = fmt.Print
|
|
_ = mypkg.Foo
|
|
)
|
|
`
|
|
output := `package p
|
|
|
|
import "fmt"
|
|
|
|
var (
|
|
_ = fmt.Print
|
|
_ = mypkg.Foo
|
|
)
|
|
`
|
|
buf, err := Process(newGoPath+"/src/myotherpkg/toformat.go", []byte(input), &Options{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := string(buf); got != output {
|
|
t.Fatalf("ignored results differ\nGOT:\n%s\nWANT:\n%s\n", got, output)
|
|
}
|
|
})
|
|
|
|
}
|
|
|
|
// 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 TestFixImportsVendorPackage(t *testing.T) {
|
|
// Skip this test on go versions with no vendor support.
|
|
if _, err := os.Stat(filepath.Join(runtime.GOROOT(), "src/vendor")); err != nil {
|
|
t.Skip(err)
|
|
}
|
|
testConfig{
|
|
gopathFiles: map[string]string{
|
|
"mypkg.com/outpkg/vendor/mypkg.com/mypkg.v1/f.go": "package mypkg\nvar Foo = 123\n",
|
|
},
|
|
}.test(t, func(t *goimportTest) {
|
|
input := `package p
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"mypkg.com/mypkg.v1"
|
|
)
|
|
|
|
var (
|
|
_ = fmt.Print
|
|
_ = mypkg.Foo
|
|
)
|
|
`
|
|
buf, err := Process(filepath.Join(t.gopath, "src/mypkg.com/outpkg/toformat.go"), []byte(input), &Options{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := string(buf); got != input {
|
|
t.Fatalf("results differ\nGOT:\n%s\nWANT:\n%s\n", got, input)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestFindImportGoPath(t *testing.T) {
|
|
goroot, err := ioutil.TempDir("", "goimports-")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer os.RemoveAll(goroot)
|
|
|
|
origStdlib := stdlib
|
|
defer func() {
|
|
stdlib = origStdlib
|
|
}()
|
|
stdlib = nil
|
|
|
|
withEmptyGoPath(func() {
|
|
// Test against imaginary bits/bytes package in std lib
|
|
bytesDir := filepath.Join(goroot, "src", "pkg", "bits", "bytes")
|
|
for _, tag := range build.Default.ReleaseTags {
|
|
// Go 1.4 rearranged the GOROOT tree to remove the "pkg" path component.
|
|
if tag == "go1.4" {
|
|
bytesDir = filepath.Join(goroot, "src", "bits", "bytes")
|
|
}
|
|
}
|
|
if err := os.MkdirAll(bytesDir, 0755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
bytesSrcPath := filepath.Join(bytesDir, "bytes.go")
|
|
bytesPkgPath := "bits/bytes"
|
|
bytesSrc := []byte(`package bytes
|
|
|
|
type Buffer2 struct {}
|
|
`)
|
|
if err := ioutil.WriteFile(bytesSrcPath, bytesSrc, 0775); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
build.Default.GOROOT = goroot
|
|
|
|
got, rename, err := findImportGoPath("bytes", map[string]bool{"Buffer2": true}, "x.go")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got != bytesPkgPath || rename {
|
|
t.Errorf(`findImportGoPath("bytes", Buffer2 ...)=%q, %t, want "%s", false`, got, rename, bytesPkgPath)
|
|
}
|
|
|
|
got, rename, err = findImportGoPath("bytes", map[string]bool{"Missing": true}, "x.go")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got != "" || rename {
|
|
t.Errorf(`findImportGoPath("bytes", Missing ...)=%q, %t, want "", false`, got, rename)
|
|
}
|
|
})
|
|
}
|
|
|
|
func init() {
|
|
inTests = true
|
|
}
|
|
|
|
func withEmptyGoPath(fn func()) {
|
|
testMu.Lock()
|
|
|
|
dirScanMu.Lock()
|
|
populateIgnoreOnce = sync.Once{}
|
|
scanGoRootOnce = sync.Once{}
|
|
scanGoPathOnce = sync.Once{}
|
|
dirScan = nil
|
|
ignoredDirs = nil
|
|
scanGoRootDone = make(chan struct{})
|
|
dirScanMu.Unlock()
|
|
|
|
oldGOPATH := build.Default.GOPATH
|
|
oldGOROOT := build.Default.GOROOT
|
|
build.Default.GOPATH = ""
|
|
testHookScanDir = func(string) {}
|
|
testMu.Unlock()
|
|
|
|
defer func() {
|
|
testMu.Lock()
|
|
testHookScanDir = func(string) {}
|
|
build.Default.GOPATH = oldGOPATH
|
|
build.Default.GOROOT = oldGOROOT
|
|
testMu.Unlock()
|
|
}()
|
|
|
|
fn()
|
|
}
|
|
|
|
func TestFindImportInternal(t *testing.T) {
|
|
withEmptyGoPath(func() {
|
|
// Check for src/internal/race, not just src/internal,
|
|
// so that we can run this test also against go1.5
|
|
// (which doesn't contain that file).
|
|
_, err := os.Stat(filepath.Join(runtime.GOROOT(), "src/internal/race"))
|
|
if err != nil {
|
|
t.Skip(err)
|
|
}
|
|
|
|
got, rename, err := findImportGoPath("race", map[string]bool{"Acquire": true}, filepath.Join(runtime.GOROOT(), "src/math/x.go"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got != "internal/race" || rename {
|
|
t.Errorf(`findImportGoPath("race", Acquire ...) = %q, %t; want "internal/race", false`, got, rename)
|
|
}
|
|
|
|
// should not be able to use internal from outside that tree
|
|
got, rename, err = findImportGoPath("race", map[string]bool{"Acquire": true}, filepath.Join(runtime.GOROOT(), "x.go"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got != "" || rename {
|
|
t.Errorf(`findImportGoPath("race", Acquire ...)=%q, %t, want "", false`, got, rename)
|
|
}
|
|
})
|
|
}
|
|
|
|
// rand.Read should prefer crypto/rand.Read, not math/rand.Read.
|
|
func TestFindImportRandRead(t *testing.T) {
|
|
withEmptyGoPath(func() {
|
|
file := filepath.Join(runtime.GOROOT(), "src/foo/x.go") // dummy
|
|
tests := []struct {
|
|
syms []string
|
|
want string
|
|
}{
|
|
{
|
|
syms: []string{"Read"},
|
|
want: "crypto/rand",
|
|
},
|
|
{
|
|
syms: []string{"Read", "NewZipf"},
|
|
want: "math/rand",
|
|
},
|
|
{
|
|
syms: []string{"NewZipf"},
|
|
want: "math/rand",
|
|
},
|
|
{
|
|
syms: []string{"Read", "Prime"},
|
|
want: "crypto/rand",
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
m := map[string]bool{}
|
|
for _, sym := range tt.syms {
|
|
m[sym] = true
|
|
}
|
|
got, _, err := findImportGoPath("rand", m, file)
|
|
if err != nil {
|
|
t.Errorf("for %q: %v", tt.syms, err)
|
|
continue
|
|
}
|
|
if got != tt.want {
|
|
t.Errorf("for %q, findImportGoPath = %q; want %q", tt.syms, got, tt.want)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestFindImportVendor(t *testing.T) {
|
|
testConfig{
|
|
gorootFiles: map[string]string{
|
|
"vendor/golang.org/x/net/http2/hpack/huffman.go": "package hpack\nfunc HuffmanDecode() { }\n",
|
|
},
|
|
}.test(t, func(t *goimportTest) {
|
|
got, rename, err := findImportGoPath("hpack", map[string]bool{"HuffmanDecode": true}, filepath.Join(t.goroot, "src/math/x.go"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
want := "golang.org/x/net/http2/hpack"
|
|
if got != want || rename {
|
|
t.Errorf(`findImportGoPath("hpack", HuffmanDecode ...) = %q, %t; want %q, false`, got, rename, want)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestProcessVendor(t *testing.T) {
|
|
withEmptyGoPath(func() {
|
|
_, err := os.Stat(filepath.Join(runtime.GOROOT(), "src/vendor"))
|
|
if err != nil {
|
|
t.Skip(err)
|
|
}
|
|
|
|
target := filepath.Join(runtime.GOROOT(), "src/math/x.go")
|
|
out, err := Process(target, []byte("package http\nimport \"bytes\"\nfunc f() { strings.NewReader(); hpack.HuffmanDecode() }\n"), nil)
|
|
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
want := "golang_org/x/net/http2/hpack"
|
|
if _, err := os.Stat(filepath.Join(runtime.GOROOT(), "src/vendor", want)); os.IsNotExist(err) {
|
|
want = "golang.org/x/net/http2/hpack"
|
|
}
|
|
|
|
if !bytes.Contains(out, []byte(want)) {
|
|
t.Fatalf("Process(%q) did not add expected hpack import %q; got:\n%s", target, want, out)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestFindImportStdlib(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 {
|
|
got, rename, ok := findImportStdlib(tt.pkg, strSet(tt.symbols))
|
|
if (got != "") != ok {
|
|
t.Error("findImportStdlib return value inconsistent")
|
|
}
|
|
if got != tt.want || rename {
|
|
t.Errorf("findImportStdlib(%q, %q) = %q, %t; want %q, false", tt.pkg, tt.symbols, got, rename, tt.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
type testConfig struct {
|
|
// goroot and gopath optionally specifies the path on disk
|
|
// to use for the GOROOT and GOPATH. If empty, a temp directory
|
|
// is made if needed.
|
|
goroot, gopath string
|
|
|
|
// gorootFiles optionally specifies the complete contents of GOROOT to use,
|
|
// If nil, the normal current $GOROOT is used.
|
|
gorootFiles map[string]string // paths relative to $GOROOT/src to contents
|
|
|
|
// gopathFiles is like gorootFiles, but for $GOPATH.
|
|
// If nil, there is no GOPATH, though.
|
|
gopathFiles map[string]string // paths relative to $GOPATH/src to contents
|
|
}
|
|
|
|
func mustTempDir(t *testing.T, prefix string) string {
|
|
dir, err := ioutil.TempDir("", prefix)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return dir
|
|
}
|
|
|
|
func mapToDir(destDir string, files map[string]string) error {
|
|
for path, contents := range files {
|
|
file := filepath.Join(destDir, "src", path)
|
|
if err := os.MkdirAll(filepath.Dir(file), 0755); err != nil {
|
|
return err
|
|
}
|
|
var err error
|
|
if strings.HasPrefix(contents, "LINK:") {
|
|
err = os.Symlink(strings.TrimPrefix(contents, "LINK:"), file)
|
|
} else {
|
|
err = ioutil.WriteFile(file, []byte(contents), 0644)
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c testConfig) test(t *testing.T, fn func(*goimportTest)) {
|
|
goroot := c.goroot
|
|
gopath := c.gopath
|
|
|
|
if c.gorootFiles != nil && goroot == "" {
|
|
goroot = mustTempDir(t, "goroot-")
|
|
defer os.RemoveAll(goroot)
|
|
}
|
|
if err := mapToDir(goroot, c.gorootFiles); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if c.gopathFiles != nil && gopath == "" {
|
|
gopath = mustTempDir(t, "gopath-")
|
|
defer os.RemoveAll(gopath)
|
|
}
|
|
if err := mapToDir(gopath, c.gopathFiles); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
withEmptyGoPath(func() {
|
|
if goroot != "" {
|
|
build.Default.GOROOT = goroot
|
|
}
|
|
build.Default.GOPATH = gopath
|
|
|
|
it := &goimportTest{
|
|
T: t,
|
|
goroot: build.Default.GOROOT,
|
|
gopath: gopath,
|
|
ctx: &build.Default,
|
|
}
|
|
fn(it)
|
|
})
|
|
}
|
|
|
|
type goimportTest struct {
|
|
*testing.T
|
|
ctx *build.Context
|
|
goroot string
|
|
gopath string
|
|
}
|
|
|
|
// Tests that added imports are renamed when the import path's base doesn't
|
|
// match its package name. For example, we want to generate:
|
|
//
|
|
// import cloudbilling "google.golang.org/api/cloudbilling/v1"
|
|
func TestRenameWhenPackageNameMismatch(t *testing.T) {
|
|
testConfig{
|
|
gopathFiles: map[string]string{
|
|
"foo/bar/v1/x.go": "package bar \n const X = 1",
|
|
},
|
|
}.test(t, func(t *goimportTest) {
|
|
buf, err := Process(t.gopath+"/src/test/t.go", []byte("package main \n const Y = bar.X"), &Options{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
const want = `package main
|
|
|
|
import bar "foo/bar/v1"
|
|
|
|
const Y = bar.X
|
|
`
|
|
if string(buf) != want {
|
|
t.Errorf("Got:\n%s\nWant:\n%s", buf, want)
|
|
}
|
|
})
|
|
}
|
|
|
|
// Tests that the LocalPrefix option causes imports
|
|
// to be added into a later group (num=3).
|
|
func TestLocalPrefix(t *testing.T) {
|
|
defer func(s string) { LocalPrefix = s }(LocalPrefix)
|
|
LocalPrefix = "foo/"
|
|
|
|
testConfig{
|
|
gopathFiles: map[string]string{
|
|
"foo/bar/bar.go": "package bar \n const X = 1",
|
|
},
|
|
}.test(t, func(t *goimportTest) {
|
|
buf, err := Process(t.gopath+"/src/test/t.go", []byte("package main \n const Y = bar.X \n const _ = runtime.GOOS"), &Options{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
const want = `package main
|
|
|
|
import (
|
|
"runtime"
|
|
|
|
"foo/bar"
|
|
)
|
|
|
|
const Y = bar.X
|
|
const _ = runtime.GOOS
|
|
`
|
|
if string(buf) != want {
|
|
t.Errorf("Got:\n%s\nWant:\n%s", buf, want)
|
|
}
|
|
})
|
|
}
|
|
|
|
// Tests that running goimport on files in GOROOT (for people hacking
|
|
// on Go itself) don't cause the GOPATH to be scanned (which might be
|
|
// much bigger).
|
|
func TestOptimizationWhenInGoroot(t *testing.T) {
|
|
testConfig{
|
|
gopathFiles: map[string]string{
|
|
"foo/foo.go": "package foo\nconst X = 1\n",
|
|
},
|
|
}.test(t, func(t *goimportTest) {
|
|
testHookScanDir = func(dir string) {
|
|
if dir != filepath.Join(build.Default.GOROOT, "src") {
|
|
t.Errorf("unexpected dir scan of %s", dir)
|
|
}
|
|
}
|
|
const in = "package foo\n\nconst Y = bar.X\n"
|
|
buf, err := Process(t.goroot+"/src/foo/foo.go", []byte(in), nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if string(buf) != in {
|
|
t.Errorf("got:\n%q\nwant unchanged:\n%q\n", in, buf)
|
|
}
|
|
})
|
|
}
|
|
|
|
// Tests that "package documentation" files are ignored.
|
|
func TestIgnoreDocumentationPackage(t *testing.T) {
|
|
testConfig{
|
|
gopathFiles: map[string]string{
|
|
"foo/foo.go": "package foo\nconst X = 1\n",
|
|
"foo/doc.go": "package documentation \n // just to confuse things\n",
|
|
},
|
|
}.test(t, func(t *goimportTest) {
|
|
const in = "package x\n\nconst Y = foo.X\n"
|
|
const want = "package x\n\nimport \"foo\"\n\nconst Y = foo.X\n"
|
|
buf, err := Process(t.gopath+"/src/x/x.go", []byte(in), nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if string(buf) != want {
|
|
t.Errorf("wrong output.\ngot:\n%q\nwant:\n%q\n", in, 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{
|
|
gopathFiles: map[string]string{
|
|
"example.net/pkg/doc.go": "package documentation\n", // ignored
|
|
"example.net/pkg/gen.go": "package main\n", // also ignored
|
|
"example.net/pkg/pkg.go": "package the_pkg_name_to_find\n and this syntax error is ignored because of parser.PackageClauseOnly",
|
|
"example.net/pkg/z.go": "package inconsistent\n", // inconsistent but ignored
|
|
},
|
|
}.test(t, func(t *goimportTest) {
|
|
got, err := importPathToNameGoPathParse("example.net/pkg", filepath.Join(t.gopath, "src", "other.net"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
const want = "the_pkg_name_to_find"
|
|
if got != want {
|
|
t.Errorf("importPathToNameGoPathParse(..) = %q; want %q", got, want)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestIgnoreConfiguration(t *testing.T) {
|
|
testConfig{
|
|
gopathFiles: map[string]string{
|
|
".goimportsignore": "# comment line\n\n example.net", // tests comment, blank line, whitespace trimming
|
|
"example.net/pkg/pkg.go": "package pkg\nconst X = 1",
|
|
"otherwise-longer-so-worse.example.net/foo/pkg/pkg.go": "package pkg\nconst X = 1",
|
|
},
|
|
}.test(t, func(t *goimportTest) {
|
|
const in = "package x\n\nconst _ = pkg.X\n"
|
|
const want = "package x\n\nimport \"otherwise-longer-so-worse.example.net/foo/pkg\"\n\nconst _ = pkg.X\n"
|
|
buf, err := Process(t.gopath+"/src/x/x.go", []byte(in), nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if string(buf) != want {
|
|
t.Errorf("wrong output.\ngot:\n%q\nwant:\n%q\n", buf, want)
|
|
}
|
|
})
|
|
}
|
|
|
|
// Skip "node_modules" directory.
|
|
func TestSkipNodeModules(t *testing.T) {
|
|
testConfig{
|
|
gopathFiles: map[string]string{
|
|
"example.net/node_modules/pkg/a.go": "package pkg\nconst X = 1",
|
|
"otherwise-longer.net/not_modules/pkg/a.go": "package pkg\nconst X = 1",
|
|
},
|
|
}.test(t, func(t *goimportTest) {
|
|
const in = "package x\n\nconst _ = pkg.X\n"
|
|
const want = "package x\n\nimport \"otherwise-longer.net/not_modules/pkg\"\n\nconst _ = pkg.X\n"
|
|
buf, err := Process(t.gopath+"/src/x/x.go", []byte(in), nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if string(buf) != want {
|
|
t.Errorf("wrong output.\ngot:\n%q\nwant:\n%q\n", buf, want)
|
|
}
|
|
})
|
|
}
|
|
|
|
// golang.org/issue/16458 -- if GOROOT is a prefix of GOPATH, GOPATH is ignored.
|
|
func TestGoRootPrefixOfGoPath(t *testing.T) {
|
|
dir := mustTempDir(t, "importstest")
|
|
defer os.RemoveAll(dir)
|
|
testConfig{
|
|
goroot: filepath.Join(dir, "go"),
|
|
gopath: filepath.Join(dir, "gopath"),
|
|
gopathFiles: map[string]string{
|
|
"example.com/foo/pkg.go": "package foo\nconst X = 1",
|
|
},
|
|
}.test(t, func(t *goimportTest) {
|
|
const in = "package x\n\nconst _ = foo.X\n"
|
|
const want = "package x\n\nimport \"example.com/foo\"\n\nconst _ = foo.X\n"
|
|
buf, err := Process(t.gopath+"/src/x/x.go", []byte(in), nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if string(buf) != want {
|
|
t.Errorf("wrong output.\ngot:\n%q\nwant:\n%q\n", buf, want)
|
|
}
|
|
})
|
|
|
|
}
|
|
|
|
const testGlobalImportsUsesGlobal = `package globalimporttest
|
|
|
|
func doSomething() {
|
|
t := time.Now()
|
|
}
|
|
`
|
|
|
|
const testGlobalImportsGlobalDecl = `package globalimporttest
|
|
|
|
type Time struct{}
|
|
|
|
func (t Time) Now() Time {
|
|
return Time{}
|
|
}
|
|
|
|
var time Time
|
|
`
|
|
|
|
// 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 pkg = "globalimporttest"
|
|
const usesGlobalFile = pkg + "/uses_global.go"
|
|
testConfig{
|
|
gopathFiles: map[string]string{
|
|
usesGlobalFile: testGlobalImportsUsesGlobal,
|
|
pkg + "/global.go": testGlobalImportsGlobalDecl,
|
|
},
|
|
}.test(t, func(t *goimportTest) {
|
|
buf, err := Process(
|
|
t.gopath+"/src/"+usesGlobalFile, []byte(testGlobalImportsUsesGlobal), nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if string(buf) != testGlobalImportsUsesGlobal {
|
|
t.Errorf("wrong output.\ngot:\n%q\nwant:\n%q\n", buf, testGlobalImportsUsesGlobal)
|
|
}
|
|
})
|
|
}
|
|
|
|
// 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"
|
|
|
|
func LogSomething() {
|
|
log.Print("Something")
|
|
}
|
|
`
|
|
|
|
// need is the file being tested that needs the import.
|
|
const need = `package siblingimporttest
|
|
|
|
func LogSomethingElse() {
|
|
log.Print("Something else")
|
|
}
|
|
`
|
|
|
|
// want is the expected result file
|
|
const want = `package siblingimporttest
|
|
|
|
import "local/log"
|
|
|
|
func LogSomethingElse() {
|
|
log.Print("Something else")
|
|
}
|
|
`
|
|
|
|
const pkg = "siblingimporttest"
|
|
const siblingFile = pkg + "/needs_import.go"
|
|
testConfig{
|
|
gopathFiles: map[string]string{
|
|
siblingFile: need,
|
|
pkg + "/provides_import.go": provide,
|
|
},
|
|
}.test(t, func(t *goimportTest) {
|
|
buf, err := Process(
|
|
t.gopath+"/src/"+siblingFile, []byte(need), nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if string(buf) != want {
|
|
t.Errorf("wrong output.\ngot:\n%q\nwant:\n%q\n", buf, want)
|
|
}
|
|
})
|
|
}
|
|
|
|
func strSet(ss []string) map[string]bool {
|
|
m := make(map[string]bool)
|
|
for _, s := range ss {
|
|
m[s] = true
|
|
}
|
|
return m
|
|
}
|
|
|
|
func TestPkgIsCandidate(t *testing.T) {
|
|
tests := [...]struct {
|
|
filename string
|
|
pkgIdent string
|
|
pkg *pkg
|
|
want bool
|
|
}{
|
|
// normal match
|
|
0: {
|
|
filename: "/gopath/src/my/pkg/pkg.go",
|
|
pkgIdent: "client",
|
|
pkg: &pkg{
|
|
dir: "/gopath/src/client",
|
|
importPath: "client",
|
|
importPathShort: "client",
|
|
},
|
|
want: true,
|
|
},
|
|
// not a match
|
|
1: {
|
|
filename: "/gopath/src/my/pkg/pkg.go",
|
|
pkgIdent: "zzz",
|
|
pkg: &pkg{
|
|
dir: "/gopath/src/client",
|
|
importPath: "client",
|
|
importPathShort: "client",
|
|
},
|
|
want: false,
|
|
},
|
|
// would be a match, but "client" appears too deep.
|
|
2: {
|
|
filename: "/gopath/src/my/pkg/pkg.go",
|
|
pkgIdent: "client",
|
|
pkg: &pkg{
|
|
dir: "/gopath/src/client/foo/foo/foo",
|
|
importPath: "client/foo/foo",
|
|
importPathShort: "client/foo/foo",
|
|
},
|
|
want: false,
|
|
},
|
|
// not an exact match, but substring is good enough.
|
|
3: {
|
|
filename: "/gopath/src/my/pkg/pkg.go",
|
|
pkgIdent: "client",
|
|
pkg: &pkg{
|
|
dir: "/gopath/src/foo/go-client",
|
|
importPath: "foo/go-client",
|
|
importPathShort: "foo/go-client",
|
|
},
|
|
want: true,
|
|
},
|
|
// "internal" package, and not visible
|
|
4: {
|
|
filename: "/gopath/src/my/pkg/pkg.go",
|
|
pkgIdent: "client",
|
|
pkg: &pkg{
|
|
dir: "/gopath/src/foo/internal/client",
|
|
importPath: "foo/internal/client",
|
|
importPathShort: "foo/internal/client",
|
|
},
|
|
want: false,
|
|
},
|
|
// "internal" package but visible
|
|
5: {
|
|
filename: "/gopath/src/foo/bar.go",
|
|
pkgIdent: "client",
|
|
pkg: &pkg{
|
|
dir: "/gopath/src/foo/internal/client",
|
|
importPath: "foo/internal/client",
|
|
importPathShort: "foo/internal/client",
|
|
},
|
|
want: true,
|
|
},
|
|
// "vendor" package not visible
|
|
6: {
|
|
filename: "/gopath/src/foo/bar.go",
|
|
pkgIdent: "client",
|
|
pkg: &pkg{
|
|
dir: "/gopath/src/other/vendor/client",
|
|
importPath: "other/vendor/client",
|
|
importPathShort: "client",
|
|
},
|
|
want: false,
|
|
},
|
|
// "vendor" package, visible
|
|
7: {
|
|
filename: "/gopath/src/foo/bar.go",
|
|
pkgIdent: "client",
|
|
pkg: &pkg{
|
|
dir: "/gopath/src/foo/vendor/client",
|
|
importPath: "other/foo/client",
|
|
importPathShort: "client",
|
|
},
|
|
want: true,
|
|
},
|
|
// Ignore hyphens.
|
|
8: {
|
|
filename: "/gopath/src/foo/bar.go",
|
|
pkgIdent: "socketio",
|
|
pkg: &pkg{
|
|
dir: "/gopath/src/foo/socket-io",
|
|
importPath: "foo/socket-io",
|
|
importPathShort: "foo/socket-io",
|
|
},
|
|
want: true,
|
|
},
|
|
// Ignore case.
|
|
9: {
|
|
filename: "/gopath/src/foo/bar.go",
|
|
pkgIdent: "fooprod",
|
|
pkg: &pkg{
|
|
dir: "/gopath/src/foo/FooPROD",
|
|
importPath: "foo/FooPROD",
|
|
importPathShort: "foo/FooPROD",
|
|
},
|
|
want: true,
|
|
},
|
|
// Ignoring both hyphens and case together.
|
|
10: {
|
|
filename: "/gopath/src/foo/bar.go",
|
|
pkgIdent: "fooprod",
|
|
pkg: &pkg{
|
|
dir: "/gopath/src/foo/Foo-PROD",
|
|
importPath: "foo/Foo-PROD",
|
|
importPathShort: "foo/Foo-PROD",
|
|
},
|
|
want: true,
|
|
},
|
|
}
|
|
for i, tt := range tests {
|
|
got := pkgIsCandidate(tt.filename, tt.pkgIdent, 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)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestShouldTraverse(t *testing.T) {
|
|
switch runtime.GOOS {
|
|
case "windows", "plan9":
|
|
t.Skipf("skipping symlink-requiring test on %s", runtime.GOOS)
|
|
}
|
|
|
|
dir, err := ioutil.TempDir("", "goimports-")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer os.RemoveAll(dir)
|
|
|
|
// Note: mapToDir prepends "src" to each element, since
|
|
// mapToDir was made for creating GOPATHs.
|
|
if err := mapToDir(dir, map[string]string{
|
|
"foo/foo2/file.txt": "",
|
|
"foo/foo2/link-to-src": "LINK:" + dir + "/src",
|
|
"foo/foo2/link-to-src-foo": "LINK:" + dir + "/src/foo",
|
|
"foo/foo2/link-to-dot": "LINK:.",
|
|
"bar/bar2/file.txt": "",
|
|
"bar/bar2/link-to-src-foo": "LINK:" + dir + "/src/foo",
|
|
|
|
"a/b/c": "LINK:" + dir + "/src/a/d",
|
|
"a/d/e": "LINK:" + dir + "/src/a/b",
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
tests := []struct {
|
|
dir string
|
|
file string
|
|
want bool
|
|
}{
|
|
{
|
|
dir: dir + "/src/foo/foo2",
|
|
file: "link-to-src-foo",
|
|
want: false, // loop
|
|
},
|
|
{
|
|
dir: dir + "/src/foo/foo2",
|
|
file: "link-to-src",
|
|
want: false, // loop
|
|
},
|
|
{
|
|
dir: dir + "/src/foo/foo2",
|
|
file: "link-to-dot",
|
|
want: false, // loop
|
|
},
|
|
{
|
|
dir: dir + "/src/bar/bar2",
|
|
file: "link-to-src-foo",
|
|
want: true, // not a loop
|
|
},
|
|
{
|
|
dir: dir + "/src/a/b/c",
|
|
file: "e",
|
|
want: false, // loop: "e" is the same as "b".
|
|
},
|
|
}
|
|
for i, tt := range tests {
|
|
fi, err := os.Stat(filepath.Join(tt.dir, tt.file))
|
|
if err != nil {
|
|
t.Errorf("%d. Stat = %v", i, err)
|
|
continue
|
|
}
|
|
got := shouldTraverse(tt.dir, fi)
|
|
if got != tt.want {
|
|
t.Errorf("%d. shouldTraverse(%q, %q) = %v; want %v", i, tt.dir, tt.file, got, tt.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Issue 20941: this used to panic on Windows.
|
|
func TestProcessStdin(t *testing.T) {
|
|
got, err := Process("<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)
|
|
}
|
|
}
|