github.com/cockroachdb/tools@v0.0.0-20230222021103-a6d27438930d/cmd/fiximports/main_test.go (about)

     1  // Copyright 2015 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // No testdata on Android.
     6  
     7  //go:build !android
     8  // +build !android
     9  
    10  package main
    11  
    12  import (
    13  	"bytes"
    14  	"log"
    15  	"os"
    16  	"path/filepath"
    17  	"runtime"
    18  	"strings"
    19  	"testing"
    20  
    21  	"golang.org/x/tools/internal/testenv"
    22  )
    23  
    24  // TODO(adonovan):
    25  // - test introduction of renaming imports.
    26  // - test induced failures of rewriteFile.
    27  
    28  // Guide to the test packages:
    29  //
    30  // new.com/one		-- canonical name for old.com/one
    31  // old.com/one		-- non-canonical; has import comment "new.com/one"
    32  // old.com/bad		-- has a parse error
    33  // fruit.io/orange	\
    34  // fruit.io/banana	 } orange -> pear -> banana -> titanic.biz/bar
    35  // fruit.io/pear	/
    36  // titanic.biz/bar	-- domain is sinking; package has jumped ship to new.com/bar
    37  // titanic.biz/foo	-- domain is sinking but package has no import comment yet
    38  
    39  var gopath = filepath.Join(cwd, "testdata")
    40  
    41  func init() {
    42  	if err := os.Setenv("GOPATH", gopath); err != nil {
    43  		log.Fatal(err)
    44  	}
    45  
    46  	// This test currently requires GOPATH mode.
    47  	// Explicitly disabling module mode should suffix, but
    48  	// we'll also turn off GOPROXY just for good measure.
    49  	if err := os.Setenv("GO111MODULE", "off"); err != nil {
    50  		log.Fatal(err)
    51  	}
    52  	if err := os.Setenv("GOPROXY", "off"); err != nil {
    53  		log.Fatal(err)
    54  	}
    55  }
    56  
    57  func TestFixImports(t *testing.T) {
    58  	if os.Getenv("GO_BUILDER_NAME") == "plan9-arm" {
    59  		t.Skipf("skipping test that times out on plan9-arm; see https://go.dev/issue/50775")
    60  	}
    61  	testenv.NeedsTool(t, "go")
    62  
    63  	defer func() {
    64  		stderr = os.Stderr
    65  		*badDomains = "code.google.com"
    66  		*replaceFlag = ""
    67  	}()
    68  
    69  	for i, test := range []struct {
    70  		packages    []string // packages to rewrite, "go list" syntax
    71  		badDomains  string   // -baddomains flag
    72  		replaceFlag string   // -replace flag
    73  		wantOK      bool
    74  		wantStderr  string
    75  		wantRewrite map[string]string
    76  	}{
    77  		// #0. No errors.
    78  		{
    79  			packages:   []string{"all"},
    80  			badDomains: "code.google.com",
    81  			wantOK:     true,
    82  			wantStderr: `
    83  testdata/src/old.com/bad/bad.go:2:43: expected 'package', found 'EOF'
    84  fruit.io/banana
    85  	fixed: old.com/one -> new.com/one
    86  	fixed: titanic.biz/bar -> new.com/bar
    87  `,
    88  			wantRewrite: map[string]string{
    89  				"$GOPATH/src/fruit.io/banana/banana.go": `package banana
    90  
    91  import (
    92  	_ "new.com/bar"
    93  	_ "new.com/one"
    94  	_ "titanic.biz/foo"
    95  )`,
    96  			},
    97  		},
    98  		// #1. No packages needed rewriting.
    99  		{
   100  			packages:   []string{"titanic.biz/...", "old.com/...", "new.com/..."},
   101  			badDomains: "code.google.com",
   102  			wantOK:     true,
   103  			wantStderr: `
   104  testdata/src/old.com/bad/bad.go:2:43: expected 'package', found 'EOF'
   105  `,
   106  		},
   107  		// #2. Some packages without import comments matched bad domains.
   108  		{
   109  			packages:   []string{"all"},
   110  			badDomains: "titanic.biz",
   111  			wantOK:     false,
   112  			wantStderr: `
   113  testdata/src/old.com/bad/bad.go:2:43: expected 'package', found 'EOF'
   114  fruit.io/banana
   115  	testdata/src/fruit.io/banana/banana.go:6: import "titanic.biz/foo"
   116  	fixed: old.com/one -> new.com/one
   117  	fixed: titanic.biz/bar -> new.com/bar
   118  	ERROR: titanic.biz/foo has no import comment
   119  	imported directly by:
   120  		fruit.io/pear
   121  	imported indirectly by:
   122  		fruit.io/orange
   123  `,
   124  			wantRewrite: map[string]string{
   125  				"$GOPATH/src/fruit.io/banana/banana.go": `package banana
   126  
   127  import (
   128  	_ "new.com/bar"
   129  	_ "new.com/one"
   130  	_ "titanic.biz/foo"
   131  )`,
   132  			},
   133  		},
   134  		// #3. The -replace flag lets user supply missing import comments.
   135  		{
   136  			packages:    []string{"all"},
   137  			replaceFlag: "titanic.biz/foo=new.com/foo",
   138  			wantOK:      true,
   139  			wantStderr: `
   140  testdata/src/old.com/bad/bad.go:2:43: expected 'package', found 'EOF'
   141  fruit.io/banana
   142  	fixed: old.com/one -> new.com/one
   143  	fixed: titanic.biz/bar -> new.com/bar
   144  	fixed: titanic.biz/foo -> new.com/foo
   145  `,
   146  			wantRewrite: map[string]string{
   147  				"$GOPATH/src/fruit.io/banana/banana.go": `package banana
   148  
   149  import (
   150  	_ "new.com/bar"
   151  	_ "new.com/foo"
   152  	_ "new.com/one"
   153  )`,
   154  			},
   155  		},
   156  		// #4. The -replace flag supports wildcards.
   157  		//     An explicit import comment takes precedence.
   158  		{
   159  			packages:    []string{"all"},
   160  			replaceFlag: "titanic.biz/...=new.com/...",
   161  			wantOK:      true,
   162  			wantStderr: `
   163  testdata/src/old.com/bad/bad.go:2:43: expected 'package', found 'EOF'
   164  fruit.io/banana
   165  	fixed: old.com/one -> new.com/one
   166  	fixed: titanic.biz/bar -> new.com/bar
   167  	fixed: titanic.biz/foo -> new.com/foo
   168  `,
   169  			wantRewrite: map[string]string{
   170  				"$GOPATH/src/fruit.io/banana/banana.go": `package banana
   171  
   172  import (
   173  	_ "new.com/bar"
   174  	_ "new.com/foo"
   175  	_ "new.com/one"
   176  )`,
   177  			},
   178  		},
   179  		// #5. The -replace flag trumps -baddomains.
   180  		{
   181  			packages:    []string{"all"},
   182  			badDomains:  "titanic.biz",
   183  			replaceFlag: "titanic.biz/foo=new.com/foo",
   184  			wantOK:      true,
   185  			wantStderr: `
   186  testdata/src/old.com/bad/bad.go:2:43: expected 'package', found 'EOF'
   187  fruit.io/banana
   188  	fixed: old.com/one -> new.com/one
   189  	fixed: titanic.biz/bar -> new.com/bar
   190  	fixed: titanic.biz/foo -> new.com/foo
   191  `,
   192  			wantRewrite: map[string]string{
   193  				"$GOPATH/src/fruit.io/banana/banana.go": `package banana
   194  
   195  import (
   196  	_ "new.com/bar"
   197  	_ "new.com/foo"
   198  	_ "new.com/one"
   199  )`,
   200  			},
   201  		},
   202  	} {
   203  		*badDomains = test.badDomains
   204  		*replaceFlag = test.replaceFlag
   205  
   206  		stderr = new(bytes.Buffer)
   207  		gotRewrite := make(map[string]string)
   208  		writeFile = func(filename string, content []byte, mode os.FileMode) error {
   209  			filename = strings.Replace(filename, gopath, "$GOPATH", 1)
   210  			filename = filepath.ToSlash(filename)
   211  			gotRewrite[filename] = string(bytes.TrimSpace(content))
   212  			return nil
   213  		}
   214  
   215  		if runtime.GOOS == "windows" {
   216  			test.wantStderr = strings.Replace(test.wantStderr, `testdata/src/old.com/bad/bad.go`, `testdata\src\old.com\bad\bad.go`, -1)
   217  			test.wantStderr = strings.Replace(test.wantStderr, `testdata/src/fruit.io/banana/banana.go`, `testdata\src\fruit.io\banana\banana.go`, -1)
   218  		}
   219  		test.wantStderr = strings.TrimSpace(test.wantStderr)
   220  
   221  		// Check status code.
   222  		if fiximports(test.packages...) != test.wantOK {
   223  			t.Errorf("#%d. fiximports() = %t", i, !test.wantOK)
   224  		}
   225  
   226  		// Compare stderr output.
   227  		if got := strings.TrimSpace(stderr.(*bytes.Buffer).String()); got != test.wantStderr {
   228  			if strings.Contains(got, "vendor/golang_org/x/text/unicode/norm") {
   229  				t.Skip("skipping known-broken test; see golang.org/issue/17417")
   230  			}
   231  			t.Errorf("#%d. stderr: got <<\n%s\n>>, want <<\n%s\n>>",
   232  				i, got, test.wantStderr)
   233  		}
   234  
   235  		// Compare rewrites.
   236  		for k, v := range gotRewrite {
   237  			if test.wantRewrite[k] != v {
   238  				t.Errorf("#%d. rewrite[%s] = <<%s>>, want <<%s>>",
   239  					i, k, v, test.wantRewrite[k])
   240  			}
   241  			delete(test.wantRewrite, k)
   242  		}
   243  		for k, v := range test.wantRewrite {
   244  			t.Errorf("#%d. rewrite[%s] missing, want <<%s>>", i, k, v)
   245  		}
   246  	}
   247  }
   248  
   249  // TestDryRun tests that the -n flag suppresses calls to writeFile.
   250  func TestDryRun(t *testing.T) {
   251  	if os.Getenv("GO_BUILDER_NAME") == "plan9-arm" {
   252  		t.Skipf("skipping test that times out on plan9-arm; see https://go.dev/issue/50775")
   253  	}
   254  	testenv.NeedsTool(t, "go")
   255  
   256  	*dryrun = true
   257  	defer func() { *dryrun = false }() // restore
   258  	stderr = new(bytes.Buffer)
   259  	writeFile = func(filename string, content []byte, mode os.FileMode) error {
   260  		t.Fatalf("writeFile(%s) called in dryrun mode", filename)
   261  		return nil
   262  	}
   263  
   264  	if !fiximports("all") {
   265  		t.Fatalf("fiximports failed: %s", stderr)
   266  	}
   267  }