// Copyright 2015 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. // No testdata on Android. // +build !android package main import ( "bytes" "log" "os" "path/filepath" "runtime" "strings" "testing" "golang.org/x/tools/internal/testenv" ) // TODO(adonovan): // - test introduction of renaming imports. // - test induced failures of rewriteFile. // Guide to the test packages: // // new.com/one -- canonical name for old.com/one // old.com/one -- non-canonical; has import comment "new.com/one" // old.com/bad -- has a parse error // fruit.io/orange \ // fruit.io/banana } orange -> pear -> banana -> titanic.biz/bar // fruit.io/pear / // titanic.biz/bar -- domain is sinking; package has jumped ship to new.com/bar // titanic.biz/foo -- domain is sinking but package has no import comment yet var gopath = filepath.Join(cwd, "testdata") func init() { if err := os.Setenv("GOPATH", gopath); err != nil { log.Fatal(err) } // This test currently requires GOPATH mode. // Explicitly disabling module mode should suffix, but // we'll also turn off GOPROXY just for good measure. if err := os.Setenv("GO111MODULE", "off"); err != nil { log.Fatal(err) } if err := os.Setenv("GOPROXY", "off"); err != nil { log.Fatal(err) } } func TestFixImports(t *testing.T) { testenv.NeedsTool(t, "go") defer func() { stderr = os.Stderr *badDomains = "code.google.com" *replaceFlag = "" }() for i, test := range []struct { packages []string // packages to rewrite, "go list" syntax badDomains string // -baddomains flag replaceFlag string // -replace flag wantOK bool wantStderr string wantRewrite map[string]string }{ // #0. No errors. { packages: []string{"all"}, badDomains: "code.google.com", wantOK: true, wantStderr: ` testdata/src/old.com/bad/bad.go:2:43: expected 'package', found 'EOF' fruit.io/banana fixed: old.com/one -> new.com/one fixed: titanic.biz/bar -> new.com/bar `, wantRewrite: map[string]string{ "$GOPATH/src/fruit.io/banana/banana.go": `package banana import ( _ "new.com/bar" _ "new.com/one" _ "titanic.biz/foo" )`, }, }, // #1. No packages needed rewriting. { packages: []string{"titanic.biz/...", "old.com/...", "new.com/..."}, badDomains: "code.google.com", wantOK: true, wantStderr: ` testdata/src/old.com/bad/bad.go:2:43: expected 'package', found 'EOF' `, }, // #2. Some packages without import comments matched bad domains. { packages: []string{"all"}, badDomains: "titanic.biz", wantOK: false, wantStderr: ` testdata/src/old.com/bad/bad.go:2:43: expected 'package', found 'EOF' fruit.io/banana testdata/src/fruit.io/banana/banana.go:6: import "titanic.biz/foo" fixed: old.com/one -> new.com/one fixed: titanic.biz/bar -> new.com/bar ERROR: titanic.biz/foo has no import comment imported directly by: fruit.io/pear imported indirectly by: fruit.io/orange `, wantRewrite: map[string]string{ "$GOPATH/src/fruit.io/banana/banana.go": `package banana import ( _ "new.com/bar" _ "new.com/one" _ "titanic.biz/foo" )`, }, }, // #3. The -replace flag lets user supply missing import comments. { packages: []string{"all"}, replaceFlag: "titanic.biz/foo=new.com/foo", wantOK: true, wantStderr: ` testdata/src/old.com/bad/bad.go:2:43: expected 'package', found 'EOF' fruit.io/banana fixed: old.com/one -> new.com/one fixed: titanic.biz/bar -> new.com/bar fixed: titanic.biz/foo -> new.com/foo `, wantRewrite: map[string]string{ "$GOPATH/src/fruit.io/banana/banana.go": `package banana import ( _ "new.com/bar" _ "new.com/foo" _ "new.com/one" )`, }, }, // #4. The -replace flag supports wildcards. // An explicit import comment takes precedence. { packages: []string{"all"}, replaceFlag: "titanic.biz/...=new.com/...", wantOK: true, wantStderr: ` testdata/src/old.com/bad/bad.go:2:43: expected 'package', found 'EOF' fruit.io/banana fixed: old.com/one -> new.com/one fixed: titanic.biz/bar -> new.com/bar fixed: titanic.biz/foo -> new.com/foo `, wantRewrite: map[string]string{ "$GOPATH/src/fruit.io/banana/banana.go": `package banana import ( _ "new.com/bar" _ "new.com/foo" _ "new.com/one" )`, }, }, // #5. The -replace flag trumps -baddomains. { packages: []string{"all"}, badDomains: "titanic.biz", replaceFlag: "titanic.biz/foo=new.com/foo", wantOK: true, wantStderr: ` testdata/src/old.com/bad/bad.go:2:43: expected 'package', found 'EOF' fruit.io/banana fixed: old.com/one -> new.com/one fixed: titanic.biz/bar -> new.com/bar fixed: titanic.biz/foo -> new.com/foo `, wantRewrite: map[string]string{ "$GOPATH/src/fruit.io/banana/banana.go": `package banana import ( _ "new.com/bar" _ "new.com/foo" _ "new.com/one" )`, }, }, } { *badDomains = test.badDomains *replaceFlag = test.replaceFlag stderr = new(bytes.Buffer) gotRewrite := make(map[string]string) writeFile = func(filename string, content []byte, mode os.FileMode) error { filename = strings.Replace(filename, gopath, "$GOPATH", 1) filename = filepath.ToSlash(filename) gotRewrite[filename] = string(bytes.TrimSpace(content)) return nil } if runtime.GOOS == "windows" { test.wantStderr = strings.Replace(test.wantStderr, `testdata/src/old.com/bad/bad.go`, `testdata\src\old.com\bad\bad.go`, -1) test.wantStderr = strings.Replace(test.wantStderr, `testdata/src/fruit.io/banana/banana.go`, `testdata\src\fruit.io\banana\banana.go`, -1) } test.wantStderr = strings.TrimSpace(test.wantStderr) // Check status code. if fiximports(test.packages...) != test.wantOK { t.Errorf("#%d. fiximports() = %t", i, !test.wantOK) } // Compare stderr output. if got := strings.TrimSpace(stderr.(*bytes.Buffer).String()); got != test.wantStderr { if strings.Contains(got, "vendor/golang_org/x/text/unicode/norm") { t.Skip("skipping known-broken test; see golang.org/issue/17417") } t.Errorf("#%d. stderr: got <<\n%s\n>>, want <<\n%s\n>>", i, got, test.wantStderr) } // Compare rewrites. for k, v := range gotRewrite { if test.wantRewrite[k] != v { t.Errorf("#%d. rewrite[%s] = <<%s>>, want <<%s>>", i, k, v, test.wantRewrite[k]) } delete(test.wantRewrite, k) } for k, v := range test.wantRewrite { t.Errorf("#%d. rewrite[%s] missing, want <<%s>>", i, k, v) } } } // TestDryRun tests that the -n flag suppresses calls to writeFile. func TestDryRun(t *testing.T) { testenv.NeedsTool(t, "go") *dryrun = true defer func() { *dryrun = false }() // restore stderr = new(bytes.Buffer) writeFile = func(filename string, content []byte, mode os.FileMode) error { t.Fatalf("writeFile(%s) called in dryrun mode", filename) return nil } if !fiximports("all") { t.Fatalf("fiximports failed: %s", stderr) } }