github.com/jd-ly/tools@v0.5.7/internal/imports/fix_test.go (about)

     1  // Copyright 2013 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package imports
     6  
     7  import (
     8  	"context"
     9  	"flag"
    10  	"fmt"
    11  	"go/build"
    12  	"io/ioutil"
    13  	"log"
    14  	"path/filepath"
    15  	"reflect"
    16  	"runtime"
    17  	"sort"
    18  	"strings"
    19  	"sync"
    20  	"testing"
    21  
    22  	"github.com/jd-ly/tools/go/packages/packagestest"
    23  	"github.com/jd-ly/tools/internal/gocommand"
    24  )
    25  
    26  var testDebug = flag.Bool("debug", false, "enable debug output")
    27  
    28  var tests = []struct {
    29  	name       string
    30  	formatOnly bool
    31  	in, out    string
    32  }{
    33  	// Adding an import to an existing parenthesized import
    34  	{
    35  		name: "factored_imports_add",
    36  		in: `package foo
    37  import (
    38    "fmt"
    39  )
    40  func bar() {
    41  var b bytes.Buffer
    42  fmt.Println(b.String())
    43  }
    44  `,
    45  		out: `package foo
    46  
    47  import (
    48  	"bytes"
    49  	"fmt"
    50  )
    51  
    52  func bar() {
    53  	var b bytes.Buffer
    54  	fmt.Println(b.String())
    55  }
    56  `,
    57  	},
    58  
    59  	// Adding an import to an existing parenthesized import,
    60  	// verifying it goes into the first section.
    61  	{
    62  		name: "factored_imports_add_first_sec",
    63  		in: `package foo
    64  import (
    65    "fmt"
    66  
    67    "github.com/golang/snappy"
    68  )
    69  func bar() {
    70  var b bytes.Buffer
    71  _ = snappy.ErrCorrupt
    72  fmt.Println(b.String())
    73  }
    74  `,
    75  		out: `package foo
    76  
    77  import (
    78  	"bytes"
    79  	"fmt"
    80  
    81  	"github.com/golang/snappy"
    82  )
    83  
    84  func bar() {
    85  	var b bytes.Buffer
    86  	_ = snappy.ErrCorrupt
    87  	fmt.Println(b.String())
    88  }
    89  `,
    90  	},
    91  
    92  	// Adding an import to an existing parenthesized import,
    93  	// verifying it goes into the first section. (test 2)
    94  	{
    95  		name: "factored_imports_add_first_sec_2",
    96  		in: `package foo
    97  import (
    98    "fmt"
    99  
   100    "github.com/golang/snappy"
   101  )
   102  func bar() {
   103  _ = math.NaN
   104  _ = fmt.Sprintf
   105  _ = snappy.ErrCorrupt
   106  }
   107  `,
   108  		out: `package foo
   109  
   110  import (
   111  	"fmt"
   112  	"math"
   113  
   114  	"github.com/golang/snappy"
   115  )
   116  
   117  func bar() {
   118  	_ = math.NaN
   119  	_ = fmt.Sprintf
   120  	_ = snappy.ErrCorrupt
   121  }
   122  `,
   123  	},
   124  
   125  	// Adding a new import line, without parens
   126  	{
   127  		name: "add_import_section",
   128  		in: `package foo
   129  func bar() {
   130  var b bytes.Buffer
   131  }
   132  `,
   133  		out: `package foo
   134  
   135  import "bytes"
   136  
   137  func bar() {
   138  	var b bytes.Buffer
   139  }
   140  `,
   141  	},
   142  
   143  	// Adding two new imports, which should make a parenthesized import decl.
   144  	{
   145  		name: "add_import_paren_section",
   146  		in: `package foo
   147  func bar() {
   148  _, _ := bytes.Buffer, zip.NewReader
   149  }
   150  `,
   151  		out: `package foo
   152  
   153  import (
   154  	"archive/zip"
   155  	"bytes"
   156  )
   157  
   158  func bar() {
   159  	_, _ := bytes.Buffer, zip.NewReader
   160  }
   161  `,
   162  	},
   163  
   164  	// Make sure we don't add things twice
   165  	{
   166  		name: "no_double_add",
   167  		in: `package foo
   168  func bar() {
   169  _, _ := bytes.Buffer, bytes.NewReader
   170  }
   171  `,
   172  		out: `package foo
   173  
   174  import "bytes"
   175  
   176  func bar() {
   177  	_, _ := bytes.Buffer, bytes.NewReader
   178  }
   179  `,
   180  	},
   181  
   182  	// Make sure we don't add packages that don't have the right exports
   183  	{
   184  		name: "no_mismatched_add",
   185  		in: `package foo
   186  
   187  func bar() {
   188  	_ := bytes.NonexistentSymbol
   189  }
   190  `,
   191  		out: `package foo
   192  
   193  func bar() {
   194  	_ := bytes.NonexistentSymbol
   195  }
   196  `,
   197  	},
   198  
   199  	// Remove unused imports, 1 of a factored block
   200  	{
   201  		name: "remove_unused_1_of_2",
   202  		in: `package foo
   203  import (
   204  "bytes"
   205  "fmt"
   206  )
   207  
   208  func bar() {
   209  _, _ := bytes.Buffer, bytes.NewReader
   210  }
   211  `,
   212  		out: `package foo
   213  
   214  import (
   215  	"bytes"
   216  )
   217  
   218  func bar() {
   219  	_, _ := bytes.Buffer, bytes.NewReader
   220  }
   221  `,
   222  	},
   223  
   224  	// Remove unused imports, 2 of 2
   225  	{
   226  		name: "remove_unused_2_of_2",
   227  		in: `package foo
   228  import (
   229  "bytes"
   230  "fmt"
   231  )
   232  
   233  func bar() {
   234  }
   235  `,
   236  		out: `package foo
   237  
   238  func bar() {
   239  }
   240  `,
   241  	},
   242  
   243  	// Remove unused imports, 1 of 1
   244  	{
   245  		name: "remove_unused_1_of_1",
   246  		in: `package foo
   247  
   248  import "fmt"
   249  
   250  func bar() {
   251  }
   252  `,
   253  		out: `package foo
   254  
   255  func bar() {
   256  }
   257  `,
   258  	},
   259  
   260  	// Don't remove empty imports.
   261  	{
   262  		name: "dont_remove_empty_imports",
   263  		in: `package foo
   264  import (
   265  _ "image/png"
   266  _ "image/jpeg"
   267  )
   268  `,
   269  		out: `package foo
   270  
   271  import (
   272  	_ "image/jpeg"
   273  	_ "image/png"
   274  )
   275  `,
   276  	},
   277  
   278  	// Don't remove dot imports.
   279  	{
   280  		name: "dont_remove_dot_imports",
   281  		in: `package foo
   282  import (
   283  . "foo"
   284  . "bar"
   285  )
   286  `,
   287  		out: `package foo
   288  
   289  import (
   290  	. "bar"
   291  	. "foo"
   292  )
   293  `,
   294  	},
   295  
   296  	// Skip refs the parser can resolve.
   297  	{
   298  		name: "skip_resolved_refs",
   299  		in: `package foo
   300  
   301  func f() {
   302  	type t struct{ Println func(string) }
   303  	fmt := t{Println: func(string) {}}
   304  	fmt.Println("foo")
   305  }
   306  `,
   307  		out: `package foo
   308  
   309  func f() {
   310  	type t struct{ Println func(string) }
   311  	fmt := t{Println: func(string) {}}
   312  	fmt.Println("foo")
   313  }
   314  `,
   315  	},
   316  
   317  	// Do not add a package we already have a resolution for.
   318  	{
   319  		name: "skip_template",
   320  		in: `package foo
   321  
   322  import "html/template"
   323  
   324  func f() { t = template.New("sometemplate") }
   325  `,
   326  		out: `package foo
   327  
   328  import "html/template"
   329  
   330  func f() { t = template.New("sometemplate") }
   331  `,
   332  	},
   333  
   334  	// Don't touch cgo
   335  	{
   336  		name: "cgo",
   337  		in: `package foo
   338  
   339  /*
   340  #include <foo.h>
   341  */
   342  import "C"
   343  `,
   344  		out: `package foo
   345  
   346  /*
   347  #include <foo.h>
   348  */
   349  import "C"
   350  `,
   351  	},
   352  
   353  	// Put some things in their own section
   354  	{
   355  		name: "make_sections",
   356  		in: `package foo
   357  
   358  import (
   359  "os"
   360  )
   361  
   362  func foo () {
   363  _, _ = os.Args, fmt.Println
   364  _, _ = snappy.ErrCorrupt, p.P
   365  }
   366  `,
   367  		out: `package foo
   368  
   369  import (
   370  	"fmt"
   371  	"os"
   372  
   373  	"github.com/golang/snappy"
   374  	"rsc.io/p"
   375  )
   376  
   377  func foo() {
   378  	_, _ = os.Args, fmt.Println
   379  	_, _ = snappy.ErrCorrupt, p.P
   380  }
   381  `,
   382  	},
   383  	// Merge import blocks, even when no additions are required.
   384  	{
   385  		name: "merge_import_blocks_no_fix",
   386  		in: `package foo
   387  
   388  import (
   389  	"fmt"
   390  )
   391  import "os"
   392  
   393  import (
   394  	"rsc.io/p"
   395  )
   396  
   397  var _, _ = os.Args, fmt.Println
   398  var _, _ = snappy.ErrCorrupt, p.P
   399  `,
   400  		out: `package foo
   401  
   402  import (
   403  	"fmt"
   404  	"os"
   405  
   406  	"github.com/golang/snappy"
   407  	"rsc.io/p"
   408  )
   409  
   410  var _, _ = os.Args, fmt.Println
   411  var _, _ = snappy.ErrCorrupt, p.P
   412  `,
   413  	},
   414  	// Delete existing empty import block
   415  	{
   416  		name: "delete_empty_import_block",
   417  		in: `package foo
   418  
   419  import ()
   420  `,
   421  		out: `package foo
   422  `,
   423  	},
   424  
   425  	// Use existing empty import block
   426  	{
   427  		name: "use_empty_import_block",
   428  		in: `package foo
   429  
   430  import ()
   431  
   432  func f() {
   433  	_ = fmt.Println
   434  }
   435  `,
   436  		out: `package foo
   437  
   438  import "fmt"
   439  
   440  func f() {
   441  	_ = fmt.Println
   442  }
   443  `,
   444  	},
   445  
   446  	// Blank line before adding new section.
   447  	{
   448  		name: "blank_line_before_new_group",
   449  		in: `package foo
   450  
   451  import (
   452  	"fmt"
   453  	"net"
   454  )
   455  
   456  func f() {
   457  	_ = net.Dial
   458  	_ = fmt.Printf
   459  	_ = snappy.ErrCorrupt
   460  }
   461  `,
   462  		out: `package foo
   463  
   464  import (
   465  	"fmt"
   466  	"net"
   467  
   468  	"github.com/golang/snappy"
   469  )
   470  
   471  func f() {
   472  	_ = net.Dial
   473  	_ = fmt.Printf
   474  	_ = snappy.ErrCorrupt
   475  }
   476  `,
   477  	},
   478  
   479  	// Blank line between standard library and third-party stuff.
   480  	{
   481  		name: "blank_line_separating_std_and_third_party",
   482  		in: `package foo
   483  
   484  import (
   485  	"github.com/golang/snappy"
   486  	"fmt"
   487  	"net"
   488  )
   489  
   490  func f() {
   491  	_ = net.Dial
   492  	_ = fmt.Printf
   493  	_ = snappy.Foo
   494  }
   495  `,
   496  		out: `package foo
   497  
   498  import (
   499  	"fmt"
   500  	"net"
   501  
   502  	"github.com/golang/snappy"
   503  )
   504  
   505  func f() {
   506  	_ = net.Dial
   507  	_ = fmt.Printf
   508  	_ = snappy.Foo
   509  }
   510  `,
   511  	},
   512  
   513  	// golang.org/issue/6884
   514  	{
   515  		name: "new_imports_before_comment",
   516  		in: `package main
   517  
   518  // A comment
   519  func main() {
   520  	fmt.Println("Hello, world")
   521  }
   522  `,
   523  		out: `package main
   524  
   525  import "fmt"
   526  
   527  // A comment
   528  func main() {
   529  	fmt.Println("Hello, world")
   530  }
   531  `,
   532  	},
   533  
   534  	// golang.org/issue/7132
   535  	{
   536  		name: "new_section_for_dotless_import",
   537  		in: `package main
   538  
   539  import (
   540  "fmt"
   541  
   542  "gu"
   543  "manypackages.com/packagea"
   544  )
   545  
   546  var (
   547  a = packagea.A
   548  b = gu.A
   549  c = fmt.Printf
   550  )
   551  `,
   552  		out: `package main
   553  
   554  import (
   555  	"fmt"
   556  
   557  	"gu"
   558  
   559  	"manypackages.com/packagea"
   560  )
   561  
   562  var (
   563  	a = packagea.A
   564  	b = gu.A
   565  	c = fmt.Printf
   566  )
   567  `,
   568  	},
   569  
   570  	{
   571  		name: "fragment_with_main",
   572  		in:   `func main(){fmt.Println("Hello, world")}`,
   573  		out: `package main
   574  
   575  import "fmt"
   576  
   577  func main() { fmt.Println("Hello, world") }
   578  `,
   579  	},
   580  
   581  	{
   582  		name: "fragment_without_main",
   583  		in:   `func notmain(){fmt.Println("Hello, world")}`,
   584  		out: `import "fmt"
   585  
   586  func notmain() { fmt.Println("Hello, world") }`,
   587  	},
   588  
   589  	// Remove first import within in a 2nd/3rd/4th/etc. section.
   590  	// golang.org/issue/7679
   591  	{
   592  		name: "remove_first_import_in_section",
   593  		in: `package main
   594  
   595  import (
   596  	"fmt"
   597  
   598  	"manypackages.com/packagea"
   599  	"manypackages.com/packageb"
   600  )
   601  
   602  func main() {
   603  	var _ = fmt.Println
   604  	//var _ = packagea.A
   605  	var _ = packageb.B
   606  }
   607  `,
   608  		out: `package main
   609  
   610  import (
   611  	"fmt"
   612  
   613  	"manypackages.com/packageb"
   614  )
   615  
   616  func main() {
   617  	var _ = fmt.Println
   618  	//var _ = packagea.A
   619  	var _ = packageb.B
   620  }
   621  `,
   622  	},
   623  
   624  	// Blank line can be added before all types of import declarations.
   625  	// golang.org/issue/7866
   626  	{
   627  		name: "new_section_for_all_kinds_of_imports",
   628  		in: `package main
   629  
   630  import (
   631  	"fmt"
   632  	renamed_packagea "manypackages.com/packagea"
   633  
   634  	. "manypackages.com/packageb"
   635  	"io"
   636  
   637  	_ "manypackages.com/packagec"
   638  	"strings"
   639  )
   640  
   641  var _, _, _, _, _ = fmt.Errorf, io.Copy, strings.Contains, renamed_packagea.A, B
   642  `,
   643  		out: `package main
   644  
   645  import (
   646  	"fmt"
   647  
   648  	renamed_packagea "manypackages.com/packagea"
   649  
   650  	"io"
   651  
   652  	. "manypackages.com/packageb"
   653  
   654  	"strings"
   655  
   656  	_ "manypackages.com/packagec"
   657  )
   658  
   659  var _, _, _, _, _ = fmt.Errorf, io.Copy, strings.Contains, renamed_packagea.A, B
   660  `,
   661  	},
   662  
   663  	// Non-idempotent comment formatting
   664  	// golang.org/issue/8035
   665  	{
   666  		name: "comments_formatted",
   667  		in: `package main
   668  
   669  import (
   670  	"fmt"                     // A
   671  	"go/ast"                  // B
   672  	_ "manypackages.com/packagec"    // C
   673  )
   674  
   675  func main() { _, _ = fmt.Print, ast.Walk }
   676  `,
   677  		out: `package main
   678  
   679  import (
   680  	"fmt"    // A
   681  	"go/ast" // B
   682  
   683  	_ "manypackages.com/packagec" // C
   684  )
   685  
   686  func main() { _, _ = fmt.Print, ast.Walk }
   687  `,
   688  	},
   689  
   690  	// Failure to delete all duplicate imports
   691  	// golang.org/issue/8459
   692  	{
   693  		name: "remove_duplicates",
   694  		in: `package main
   695  
   696  import (
   697  	"fmt"
   698  	"log"
   699  	"log"
   700  	"math"
   701  )
   702  
   703  func main() { fmt.Println("pi:", math.Pi) }
   704  `,
   705  		out: `package main
   706  
   707  import (
   708  	"fmt"
   709  	"math"
   710  )
   711  
   712  func main() { fmt.Println("pi:", math.Pi) }
   713  `,
   714  	},
   715  
   716  	// Too aggressive prefix matching
   717  	// golang.org/issue/9961
   718  	{
   719  		name: "no_extra_groups",
   720  		in: `package p
   721  
   722  import (
   723  	"zip"
   724  
   725  	"rsc.io/p"
   726  )
   727  
   728  var (
   729  	_ = fmt.Print
   730  	_ = zip.Store
   731  	_ p.P
   732  	_ = regexp.Compile
   733  )
   734  `,
   735  		out: `package p
   736  
   737  import (
   738  	"fmt"
   739  	"regexp"
   740  	"zip"
   741  
   742  	"rsc.io/p"
   743  )
   744  
   745  var (
   746  	_ = fmt.Print
   747  	_ = zip.Store
   748  	_ p.P
   749  	_ = regexp.Compile
   750  )
   751  `,
   752  	},
   753  
   754  	// Unused named import is mistaken for unnamed import
   755  	// golang.org/issue/8149
   756  	{
   757  		name: "named_import_doesnt_provide_package_name",
   758  		in: `package main
   759  
   760  import foo "fmt"
   761  
   762  func main() { fmt.Println() }
   763  `,
   764  		out: `package main
   765  
   766  import "fmt"
   767  
   768  func main() { fmt.Println() }
   769  `,
   770  	},
   771  
   772  	// Unused named import is mistaken for unnamed import
   773  	// golang.org/issue/8149
   774  	{
   775  		name: "unused_named_import_removed",
   776  		in: `package main
   777  
   778  import (
   779  	"fmt"
   780  	x "fmt"
   781  )
   782  
   783  func main() { fmt.Println() }
   784  `,
   785  		out: `package main
   786  
   787  import (
   788  	"fmt"
   789  )
   790  
   791  func main() { fmt.Println() }
   792  `,
   793  	},
   794  
   795  	{
   796  		name: "ignore_unexported_identifier",
   797  		in: `package main
   798  var _ = fmt.unexported`,
   799  		out: `package main
   800  
   801  var _ = fmt.unexported
   802  `,
   803  	},
   804  
   805  	// FormatOnly
   806  	{
   807  		name:       "formatonly_works",
   808  		formatOnly: true,
   809  		in: `package main
   810  
   811  import (
   812  "fmt"
   813  "manypackages.com/packagea"
   814  )
   815  
   816  func main() {}
   817  `,
   818  		out: `package main
   819  
   820  import (
   821  	"fmt"
   822  
   823  	"manypackages.com/packagea"
   824  )
   825  
   826  func main() {}
   827  `,
   828  	},
   829  
   830  	{
   831  		name: "preserve_import_group",
   832  		in: `package p
   833  
   834  import (
   835  	"bytes"
   836  	"fmt"
   837  )
   838  
   839  var _ = fmt.Sprintf
   840  `,
   841  		out: `package p
   842  
   843  import (
   844  	"fmt"
   845  )
   846  
   847  var _ = fmt.Sprintf
   848  `,
   849  	},
   850  	{
   851  		name: "import_grouping_not_path_dependent_no_groups",
   852  		in: `package main
   853  
   854  import (
   855  	"time"
   856  )
   857  
   858  func main() {
   859  	_ = snappy.ErrCorrupt
   860  	_ = p.P
   861  	_ = time.Parse
   862  }
   863  `,
   864  		out: `package main
   865  
   866  import (
   867  	"time"
   868  
   869  	"github.com/golang/snappy"
   870  	"rsc.io/p"
   871  )
   872  
   873  func main() {
   874  	_ = snappy.ErrCorrupt
   875  	_ = p.P
   876  	_ = time.Parse
   877  }
   878  `,
   879  	},
   880  
   881  	{
   882  		name: "import_grouping_not_path_dependent_existing_group",
   883  		in: `package main
   884  
   885  import (
   886  	"time"
   887  
   888  	"github.com/golang/snappy"
   889  )
   890  
   891  func main() {
   892  	_ = snappy.ErrCorrupt
   893  	_ = p.P
   894  	_ = time.Parse
   895  }
   896  `,
   897  		out: `package main
   898  
   899  import (
   900  	"time"
   901  
   902  	"github.com/golang/snappy"
   903  	"rsc.io/p"
   904  )
   905  
   906  func main() {
   907  	_ = snappy.ErrCorrupt
   908  	_ = p.P
   909  	_ = time.Parse
   910  }
   911  `,
   912  	},
   913  
   914  	// golang.org/issue/12097
   915  	{
   916  		name: "package_statement_insertion_preserves_comments",
   917  		in: `// a
   918  // b
   919  // c
   920  
   921  func main() {
   922      _ = fmt.Println
   923  }`,
   924  		out: `package main
   925  
   926  import "fmt"
   927  
   928  // a
   929  // b
   930  // c
   931  
   932  func main() {
   933  	_ = fmt.Println
   934  }
   935  `,
   936  	},
   937  
   938  	{
   939  		name: "import_comment_stays_on_import",
   940  		in: `package main
   941  
   942  import (
   943  	"math" // fun
   944  )
   945  
   946  func main() {
   947  	x := math.MaxInt64
   948  	fmt.Println(strings.Join(",", []string{"hi"}), x)
   949  }`,
   950  		out: `package main
   951  
   952  import (
   953  	"fmt"
   954  	"math" // fun
   955  	"strings"
   956  )
   957  
   958  func main() {
   959  	x := math.MaxInt64
   960  	fmt.Println(strings.Join(",", []string{"hi"}), x)
   961  }
   962  `,
   963  	},
   964  
   965  	{
   966  		name: "no_blank_after_comment",
   967  		in: `package main
   968  
   969  import (
   970  	_ "io"
   971  	_ "net/http"
   972  	_ "net/http/pprof" // install the pprof http handlers
   973  	_ "strings"
   974  )
   975  
   976  func main() {
   977  }
   978  `,
   979  		out: `package main
   980  
   981  import (
   982  	_ "io"
   983  	_ "net/http"
   984  	_ "net/http/pprof" // install the pprof http handlers
   985  	_ "strings"
   986  )
   987  
   988  func main() {
   989  }
   990  `,
   991  	},
   992  
   993  	{
   994  		name: "no_blank_after_comment_reordered",
   995  		in: `package main
   996  
   997  import (
   998  	_ "io"
   999  	_ "net/http/pprof" // install the pprof http handlers
  1000  	_ "net/http"
  1001  	_ "strings"
  1002  )
  1003  
  1004  func main() {
  1005  }
  1006  `,
  1007  		out: `package main
  1008  
  1009  import (
  1010  	_ "io"
  1011  	_ "net/http"
  1012  	_ "net/http/pprof" // install the pprof http handlers
  1013  	_ "strings"
  1014  )
  1015  
  1016  func main() {
  1017  }
  1018  `,
  1019  	},
  1020  
  1021  	{
  1022  		name: "no_blank_after_comment_unnamed",
  1023  		in: `package main
  1024  
  1025  import (
  1026  	"encoding/json"
  1027  	"io"
  1028  	"net/http"
  1029  	_ "net/http/pprof" // install the pprof http handlers
  1030  	"strings"
  1031  
  1032  	"manypackages.com/packagea"
  1033  )
  1034  
  1035  func main() {
  1036  	_ = strings.ToUpper("hello")
  1037  	_ = io.EOF
  1038  	var (
  1039  		_ json.Number
  1040  		_ *http.Request
  1041  		_ packagea.A
  1042  	)
  1043  }
  1044  `,
  1045  		out: `package main
  1046  
  1047  import (
  1048  	"encoding/json"
  1049  	"io"
  1050  	"net/http"
  1051  	_ "net/http/pprof" // install the pprof http handlers
  1052  	"strings"
  1053  
  1054  	"manypackages.com/packagea"
  1055  )
  1056  
  1057  func main() {
  1058  	_ = strings.ToUpper("hello")
  1059  	_ = io.EOF
  1060  	var (
  1061  		_ json.Number
  1062  		_ *http.Request
  1063  		_ packagea.A
  1064  	)
  1065  }
  1066  `,
  1067  	},
  1068  
  1069  	{
  1070  		name: "blank_after_package_statement_with_comment",
  1071  		in: `package p // comment
  1072  
  1073  import "math"
  1074  
  1075  var _ = fmt.Printf
  1076  `,
  1077  		out: `package p // comment
  1078  
  1079  import "fmt"
  1080  
  1081  var _ = fmt.Printf
  1082  `,
  1083  	},
  1084  
  1085  	{
  1086  		name: "blank_after_package_statement_no_comment",
  1087  		in: `package p
  1088  
  1089  import "math"
  1090  
  1091  var _ = fmt.Printf
  1092  `,
  1093  		out: `package p
  1094  
  1095  import "fmt"
  1096  
  1097  var _ = fmt.Printf
  1098  `,
  1099  	},
  1100  
  1101  	{
  1102  		name: "cryptorand_preferred_easy_possible",
  1103  		in: `package p
  1104  
  1105  var _ = rand.Read
  1106  `,
  1107  		out: `package p
  1108  
  1109  import "crypto/rand"
  1110  
  1111  var _ = rand.Read
  1112  `,
  1113  	},
  1114  
  1115  	{
  1116  		name: "cryptorand_preferred_easy_impossible",
  1117  		in: `package p
  1118  
  1119  var _ = rand.NewZipf
  1120  `,
  1121  		out: `package p
  1122  
  1123  import "math/rand"
  1124  
  1125  var _ = rand.NewZipf
  1126  `,
  1127  	},
  1128  
  1129  	{
  1130  		name: "cryptorand_preferred_complex_possible",
  1131  		in: `package p
  1132  
  1133  var _, _ = rand.Read, rand.Prime
  1134  `,
  1135  		out: `package p
  1136  
  1137  import "crypto/rand"
  1138  
  1139  var _, _ = rand.Read, rand.Prime
  1140  `,
  1141  	},
  1142  
  1143  	{
  1144  		name: "cryptorand_preferred_complex_impossible",
  1145  		in: `package p
  1146  
  1147  var _, _ = rand.Read, rand.NewZipf
  1148  `,
  1149  		out: `package p
  1150  
  1151  import "math/rand"
  1152  
  1153  var _, _ = rand.Read, rand.NewZipf
  1154  `,
  1155  	},
  1156  }
  1157  
  1158  func TestSimpleCases(t *testing.T) {
  1159  	const localPrefix = "local.com,github.com/local"
  1160  	for _, tt := range tests {
  1161  		t.Run(tt.name, func(t *testing.T) {
  1162  			testConfig{
  1163  				modules: []packagestest.Module{
  1164  					{
  1165  						Name:  "golang.org/fake",
  1166  						Files: fm{"x.go": tt.in},
  1167  					},
  1168  					// Skeleton non-stdlib packages for use during testing.
  1169  					// Each includes one arbitrary symbol, e.g. the first declaration in the first file.
  1170  					// Try not to add more without a good reason.
  1171  					// DO NOT USE PACKAGES NOT LISTED HERE -- they will be downloaded!
  1172  					{
  1173  						Name:  "rsc.io",
  1174  						Files: fm{"p/x.go": "package p\nfunc P(){}\n"},
  1175  					},
  1176  					{
  1177  						Name:  "github.com/golang/snappy",
  1178  						Files: fm{"x.go": "package snappy\nvar ErrCorrupt error\n"},
  1179  					},
  1180  					{
  1181  						Name: "manypackages.com",
  1182  						Files: fm{
  1183  							"packagea/x.go": "package packagea\nfunc A(){}\n",
  1184  							"packageb/x.go": "package packageb\nfunc B(){}\n",
  1185  							"packagec/x.go": "package packagec\nfunc C(){}\n",
  1186  							"packaged/x.go": "package packaged\nfunc D(){}\n",
  1187  						},
  1188  					},
  1189  					{
  1190  						Name:  "local.com",
  1191  						Files: fm{"foo/x.go": "package foo\nfunc Foo(){}\n"},
  1192  					},
  1193  					{
  1194  						Name:  "github.com/local",
  1195  						Files: fm{"bar/x.go": "package bar\nfunc Bar(){}\n"},
  1196  					},
  1197  				},
  1198  			}.test(t, func(t *goimportTest) {
  1199  				options := &Options{
  1200  					LocalPrefix: localPrefix,
  1201  					TabWidth:    8,
  1202  					TabIndent:   true,
  1203  					Comments:    true,
  1204  					Fragment:    true,
  1205  					FormatOnly:  tt.formatOnly,
  1206  				}
  1207  				t.assertProcessEquals("golang.org/fake", "x.go", nil, options, tt.out)
  1208  			})
  1209  
  1210  		})
  1211  	}
  1212  }
  1213  
  1214  func TestAppengine(t *testing.T) {
  1215  	const input = `package p
  1216  
  1217  var _, _, _ = fmt.Printf, appengine.Main, datastore.ErrInvalidEntityType
  1218  `
  1219  
  1220  	const want = `package p
  1221  
  1222  import (
  1223  	"fmt"
  1224  
  1225  	"appengine"
  1226  	"appengine/datastore"
  1227  )
  1228  
  1229  var _, _, _ = fmt.Printf, appengine.Main, datastore.ErrInvalidEntityType
  1230  `
  1231  
  1232  	testConfig{
  1233  		gopathOnly: true, // can't create a module named appengine, so no module tests.
  1234  		modules: []packagestest.Module{
  1235  			{
  1236  				Name:  "golang.org/fake",
  1237  				Files: fm{"x.go": input},
  1238  			},
  1239  			{
  1240  				Name: "appengine",
  1241  				Files: fm{
  1242  					"x.go":           "package appengine\nfunc Main(){}\n",
  1243  					"datastore/x.go": "package datastore\nvar ErrInvalidEntityType error\n",
  1244  				},
  1245  			},
  1246  		},
  1247  	}.processTest(t, "golang.org/fake", "x.go", nil, nil, want)
  1248  }
  1249  
  1250  func TestReadFromFilesystem(t *testing.T) {
  1251  	tests := []struct {
  1252  		name    string
  1253  		in, out string
  1254  	}{
  1255  		{
  1256  			name: "works",
  1257  			in: `package foo
  1258  func bar() {
  1259  fmt.Println("hi")
  1260  }
  1261  `,
  1262  			out: `package foo
  1263  
  1264  import "fmt"
  1265  
  1266  func bar() {
  1267  	fmt.Println("hi")
  1268  }
  1269  `,
  1270  		},
  1271  		{
  1272  			name: "missing_package",
  1273  			in: `
  1274  func bar() {
  1275  fmt.Println("hi")
  1276  }
  1277  `,
  1278  			out: `
  1279  import "fmt"
  1280  
  1281  func bar() {
  1282  	fmt.Println("hi")
  1283  }
  1284  `,
  1285  		},
  1286  	}
  1287  
  1288  	for _, tt := range tests {
  1289  		t.Run(tt.name, func(t *testing.T) {
  1290  			options := &Options{
  1291  				TabWidth:  8,
  1292  				TabIndent: true,
  1293  				Comments:  true,
  1294  				Fragment:  true,
  1295  			}
  1296  			testConfig{
  1297  				module: packagestest.Module{
  1298  					Name:  "golang.org/fake",
  1299  					Files: fm{"x.go": tt.in},
  1300  				},
  1301  			}.processTest(t, "golang.org/fake", "x.go", nil, options, tt.out)
  1302  		})
  1303  	}
  1304  
  1305  }
  1306  
  1307  // Test support for packages in GOPATH that are actually symlinks.
  1308  // Also test that a symlink loop does not block the process.
  1309  func TestImportSymlinks(t *testing.T) {
  1310  	switch runtime.GOOS {
  1311  	case "windows", "plan9":
  1312  		t.Skipf("skipping test on %q as there are no symlinks", runtime.GOOS)
  1313  	}
  1314  
  1315  	const input = `package p
  1316  
  1317  var (
  1318  	_ = fmt.Print
  1319  	_ = mypkg.Foo
  1320  )
  1321  `
  1322  	const want = `package p
  1323  
  1324  import (
  1325  	"fmt"
  1326  
  1327  	"golang.org/fake/x/y/mypkg"
  1328  )
  1329  
  1330  var (
  1331  	_ = fmt.Print
  1332  	_ = mypkg.Foo
  1333  )
  1334  `
  1335  
  1336  	testConfig{
  1337  		module: packagestest.Module{
  1338  			Name: "golang.org/fake",
  1339  			Files: fm{
  1340  				"target/f.go":                "package mypkg\nvar Foo = 123\n",
  1341  				"x/y/mypkg":                  packagestest.Symlink("../../target"), // valid symlink
  1342  				"x/y/apkg":                   packagestest.Symlink(".."),           // symlink loop
  1343  				"myotherpackage/toformat.go": input,
  1344  			},
  1345  		},
  1346  	}.processTest(t, "golang.org/fake", "myotherpackage/toformat.go", nil, nil, want)
  1347  }
  1348  
  1349  func TestImportSymlinksWithIgnore(t *testing.T) {
  1350  	switch runtime.GOOS {
  1351  	case "windows", "plan9":
  1352  		t.Skipf("skipping test on %q as there are no symlinks", runtime.GOOS)
  1353  	}
  1354  
  1355  	const input = `package p
  1356  
  1357  var (
  1358  	_ = fmt.Print
  1359  	_ = mypkg.Foo
  1360  )
  1361  `
  1362  	const want = `package p
  1363  
  1364  import "fmt"
  1365  
  1366  var (
  1367  	_ = fmt.Print
  1368  	_ = mypkg.Foo
  1369  )
  1370  `
  1371  
  1372  	testConfig{
  1373  		gopathOnly: true,
  1374  		module: packagestest.Module{
  1375  			Name: "golang.org/fake",
  1376  			Files: fm{
  1377  				"target/f.go":            "package mypkg\nvar Foo = 123\n",
  1378  				"x/y/mypkg":              packagestest.Symlink("../../target"), // valid symlink
  1379  				"x/y/apkg":               packagestest.Symlink(".."),           // symlink loop
  1380  				"myotherpkg/toformat.go": input,
  1381  				"../../.goimportsignore": "golang.org/fake/x/y/mypkg\n",
  1382  			},
  1383  		},
  1384  	}.processTest(t, "golang.org/fake", "myotherpkg/toformat.go", nil, nil, want)
  1385  }
  1386  
  1387  // Test for x/y/v2 convention for package y.
  1388  func TestModuleVersion(t *testing.T) {
  1389  	const input = `package p
  1390  
  1391  import (
  1392  	"fmt"
  1393  
  1394  	"github.com/foo/v2"
  1395  )
  1396  
  1397  var (
  1398  	_ = fmt.Print
  1399  	_ = foo.Foo
  1400  )
  1401  `
  1402  
  1403  	testConfig{
  1404  		modules: []packagestest.Module{
  1405  			{
  1406  				Name:  "mypkg.com/outpkg",
  1407  				Files: fm{"toformat.go": input},
  1408  			},
  1409  			{
  1410  				Name:  "github.com/foo/v2",
  1411  				Files: fm{"x.go": "package foo\n func Foo(){}\n"},
  1412  			},
  1413  		},
  1414  	}.processTest(t, "mypkg.com/outpkg", "toformat.go", nil, nil, input)
  1415  }
  1416  
  1417  // Test for correctly identifying the name of a vendored package when it
  1418  // differs from its directory name. In this test, the import line
  1419  // "mypkg.com/mypkg_v1" would be removed if goimports wasn't able to detect
  1420  // that the package name is "mypkg".
  1421  func TestVendorPackage(t *testing.T) {
  1422  	const input = `package p
  1423  import (
  1424  	"fmt"
  1425  	"mypkg.com/mypkg_v1"
  1426  )
  1427  var _, _ = fmt.Print, mypkg.Foo
  1428  `
  1429  
  1430  	const want = `package p
  1431  
  1432  import (
  1433  	"fmt"
  1434  
  1435  	mypkg "mypkg.com/mypkg_v1"
  1436  )
  1437  
  1438  var _, _ = fmt.Print, mypkg.Foo
  1439  `
  1440  
  1441  	testConfig{
  1442  		gopathOnly: true,
  1443  		module: packagestest.Module{
  1444  			Name: "mypkg.com/outpkg",
  1445  			Files: fm{
  1446  				"vendor/mypkg.com/mypkg_v1/f.go": "package mypkg\nvar Foo = 123\n",
  1447  				"toformat.go":                    input,
  1448  			},
  1449  		},
  1450  	}.processTest(t, "mypkg.com/outpkg", "toformat.go", nil, nil, want)
  1451  }
  1452  
  1453  func TestInternal(t *testing.T) {
  1454  	const input = `package bar
  1455  
  1456  var _ = race.Acquire
  1457  `
  1458  	const importAdded = `package bar
  1459  
  1460  import "foo.com/internal/race"
  1461  
  1462  var _ = race.Acquire
  1463  `
  1464  
  1465  	// Packages under the same directory should be able to use internal packages.
  1466  	testConfig{
  1467  		module: packagestest.Module{
  1468  			Name: "foo.com",
  1469  			Files: fm{
  1470  				"internal/race/x.go": "package race\n func Acquire(){}\n",
  1471  				"bar/x.go":           input,
  1472  			},
  1473  		},
  1474  	}.processTest(t, "foo.com", "bar/x.go", nil, nil, importAdded)
  1475  
  1476  	// Packages outside the same directory should not.
  1477  	testConfig{
  1478  		modules: []packagestest.Module{
  1479  			{
  1480  				Name:  "foo.com",
  1481  				Files: fm{"internal/race/x.go": "package race\n func Acquire(){}\n"},
  1482  			},
  1483  			{
  1484  				Name:  "bar.com",
  1485  				Files: fm{"x.go": input},
  1486  			},
  1487  		},
  1488  	}.processTest(t, "bar.com", "x.go", nil, nil, input)
  1489  }
  1490  
  1491  func TestProcessVendor(t *testing.T) {
  1492  	const input = `package p
  1493  
  1494  var _ = hpack.HuffmanDecode
  1495  `
  1496  	const want = `package p
  1497  
  1498  import "golang.org/x/net/http2/hpack"
  1499  
  1500  var _ = hpack.HuffmanDecode
  1501  `
  1502  	testConfig{
  1503  		gopathOnly: true,
  1504  		module: packagestest.Module{
  1505  			Name: "foo.com",
  1506  			Files: fm{
  1507  				"vendor/golang.org/x/net/http2/hpack/huffman.go": "package hpack\nfunc HuffmanDecode() { }\n",
  1508  				"bar/x.go": input,
  1509  			},
  1510  		},
  1511  	}.processTest(t, "foo.com", "bar/x.go", nil, nil, want)
  1512  }
  1513  
  1514  func TestFindStdlib(t *testing.T) {
  1515  	tests := []struct {
  1516  		pkg     string
  1517  		symbols []string
  1518  		want    string
  1519  	}{
  1520  		{"http", []string{"Get"}, "net/http"},
  1521  		{"http", []string{"Get", "Post"}, "net/http"},
  1522  		{"http", []string{"Get", "Foo"}, ""},
  1523  		{"bytes", []string{"Buffer"}, "bytes"},
  1524  		{"ioutil", []string{"Discard"}, "io/ioutil"},
  1525  	}
  1526  	for _, tt := range tests {
  1527  		input := "package p\n"
  1528  		for _, sym := range tt.symbols {
  1529  			input += fmt.Sprintf("var _ = %s.%s\n", tt.pkg, sym)
  1530  		}
  1531  		testConfig{
  1532  			module: packagestest.Module{
  1533  				Name:  "foo.com",
  1534  				Files: fm{"x.go": input},
  1535  			},
  1536  		}.test(t, func(t *goimportTest) {
  1537  			buf, err := t.process("foo.com", "x.go", nil, nil)
  1538  			if err != nil {
  1539  				t.Fatal(err)
  1540  			}
  1541  			if got := string(buf); !strings.Contains(got, tt.want) {
  1542  				t.Errorf("Process(%q) = %q, wanted it to contain %q", input, buf, tt.want)
  1543  			}
  1544  		})
  1545  	}
  1546  }
  1547  
  1548  // https://golang.org/issue/31814
  1549  func TestStdlibNotPrefixed(t *testing.T) {
  1550  	const input = `package p
  1551  var _ = bytes.Buffer
  1552  `
  1553  	const want = `package p
  1554  
  1555  import "bytes"
  1556  
  1557  var _ = bytes.Buffer
  1558  `
  1559  	// Force a scan of the stdlib.
  1560  	savedStdlib := stdlib
  1561  	defer func() { stdlib = savedStdlib }()
  1562  	stdlib = map[string][]string{}
  1563  
  1564  	testConfig{
  1565  		module: packagestest.Module{
  1566  			Name:  "ignored.com",
  1567  			Files: fm{"x.go": "package x"},
  1568  		},
  1569  	}.test(t, func(t *goimportTest) {
  1570  		// Run in GOROOT/src so that the std module shows up in go list -m all.
  1571  		t.env.WorkingDir = filepath.Join(t.goroot, "src")
  1572  		got, err := t.processNonModule(filepath.Join(t.goroot, "src/x.go"), []byte(input), nil)
  1573  		if err != nil {
  1574  			t.Fatalf("Process() = %v", err)
  1575  		}
  1576  		if string(got) != want {
  1577  			t.Errorf("Got:\n%s\nWant:\n%s", got, want)
  1578  		}
  1579  	})
  1580  }
  1581  
  1582  func TestStdlibSelfImports(t *testing.T) {
  1583  	const input = `package ecdsa
  1584  
  1585  var _ = ecdsa.GenerateKey
  1586  `
  1587  
  1588  	testConfig{
  1589  		module: packagestest.Module{
  1590  			Name:  "ignored.com",
  1591  			Files: fm{"x.go": "package x"},
  1592  		},
  1593  	}.test(t, func(t *goimportTest) {
  1594  		got, err := t.processNonModule(filepath.Join(t.goroot, "src/crypto/ecdsa/foo.go"), []byte(input), nil)
  1595  		if err != nil {
  1596  			t.Fatalf("Process() = %v", err)
  1597  		}
  1598  		if string(got) != input {
  1599  			t.Errorf("Got:\n%s\nWant:\n%s", got, input)
  1600  		}
  1601  	})
  1602  }
  1603  
  1604  type testConfig struct {
  1605  	gopathOnly bool
  1606  	module     packagestest.Module
  1607  	modules    []packagestest.Module
  1608  }
  1609  
  1610  // fm is the type for a packagestest.Module's Files, abbreviated for shorter lines.
  1611  type fm map[string]interface{}
  1612  
  1613  func (c testConfig) test(t *testing.T, fn func(*goimportTest)) {
  1614  	t.Helper()
  1615  
  1616  	if c.module.Name != "" {
  1617  		c.modules = []packagestest.Module{c.module}
  1618  	}
  1619  
  1620  	for _, exporter := range packagestest.All {
  1621  		t.Run(exporter.Name(), func(t *testing.T) {
  1622  			t.Helper()
  1623  			if c.gopathOnly && exporter.Name() == "Modules" {
  1624  				t.Skip("test marked GOPATH-only")
  1625  			}
  1626  			exported := packagestest.Export(t, exporter, c.modules)
  1627  			defer exported.Cleanup()
  1628  
  1629  			env := map[string]string{}
  1630  			for _, kv := range exported.Config.Env {
  1631  				split := strings.SplitN(kv, "=", 2)
  1632  				env[split[0]] = split[1]
  1633  			}
  1634  			it := &goimportTest{
  1635  				T: t,
  1636  				env: &ProcessEnv{
  1637  					Env:         env,
  1638  					WorkingDir:  exported.Config.Dir,
  1639  					GocmdRunner: &gocommand.Runner{},
  1640  				},
  1641  				exported: exported,
  1642  			}
  1643  			if *testDebug {
  1644  				it.env.Logf = log.Printf
  1645  			}
  1646  			// packagestest clears out GOROOT to work around golang/go#32849,
  1647  			// which isn't relevant here. Fill it back in so we can find the standard library.
  1648  			it.env.Env["GOROOT"] = build.Default.GOROOT
  1649  			it.goroot = build.Default.GOROOT
  1650  
  1651  			fn(it)
  1652  		})
  1653  	}
  1654  }
  1655  
  1656  func (c testConfig) processTest(t *testing.T, module, file string, contents []byte, opts *Options, want string) {
  1657  	t.Helper()
  1658  	c.test(t, func(t *goimportTest) {
  1659  		t.Helper()
  1660  		t.assertProcessEquals(module, file, contents, opts, want)
  1661  	})
  1662  }
  1663  
  1664  type goimportTest struct {
  1665  	*testing.T
  1666  	goroot   string
  1667  	env      *ProcessEnv
  1668  	exported *packagestest.Exported
  1669  }
  1670  
  1671  func (t *goimportTest) process(module, file string, contents []byte, opts *Options) ([]byte, error) {
  1672  	t.Helper()
  1673  	f := t.exported.File(module, file)
  1674  	if f == "" {
  1675  		t.Fatalf("%v not found in exported files (typo in filename?)", file)
  1676  	}
  1677  	return t.processNonModule(f, contents, opts)
  1678  }
  1679  
  1680  func (t *goimportTest) processNonModule(file string, contents []byte, opts *Options) ([]byte, error) {
  1681  	if contents == nil {
  1682  		var err error
  1683  		contents, err = ioutil.ReadFile(file)
  1684  		if err != nil {
  1685  			return nil, err
  1686  		}
  1687  	}
  1688  	if opts == nil {
  1689  		opts = &Options{Comments: true, TabIndent: true, TabWidth: 8}
  1690  	}
  1691  	// ProcessEnv is not safe for concurrent use. Make a copy.
  1692  	opts.Env = t.env.CopyConfig()
  1693  	return Process(file, contents, opts)
  1694  }
  1695  
  1696  func (t *goimportTest) assertProcessEquals(module, file string, contents []byte, opts *Options, want string) {
  1697  	buf, err := t.process(module, file, contents, opts)
  1698  	if err != nil {
  1699  		t.Fatalf("Process() = %v", err)
  1700  	}
  1701  	if string(buf) != want {
  1702  		t.Errorf("Got:\n%s\nWant:\n%s", buf, want)
  1703  	}
  1704  }
  1705  
  1706  // Tests that added imports are renamed when the import path's base doesn't
  1707  // match its package name.
  1708  func TestRenameWhenPackageNameMismatch(t *testing.T) {
  1709  	const input = `package main
  1710   const Y = bar.X`
  1711  
  1712  	const want = `package main
  1713  
  1714  import bar "foo.com/foo/bar/baz"
  1715  
  1716  const Y = bar.X
  1717  `
  1718  	testConfig{
  1719  		module: packagestest.Module{
  1720  			Name: "foo.com",
  1721  			Files: fm{
  1722  				"foo/bar/baz/x.go": "package bar \n const X = 1",
  1723  				"test/t.go":        input,
  1724  			},
  1725  		},
  1726  	}.processTest(t, "foo.com", "test/t.go", nil, nil, want)
  1727  }
  1728  
  1729  // Tests that an existing import with badly mismatched path/name has its name
  1730  // correctly added. See #28645 and #29041.
  1731  func TestAddNameToMismatchedImport(t *testing.T) {
  1732  	const input = `package main
  1733  
  1734  import (
  1735  "foo.com/a.thing"
  1736  "foo.com/surprise"
  1737  "foo.com/v1"
  1738  "foo.com/other/v2"
  1739  "foo.com/other/v3"
  1740  "foo.com/go-thing"
  1741  "foo.com/go-wrong"
  1742  )
  1743  
  1744  var _ = []interface{}{bar.X, v1.Y, a.A, v2.V2, other.V3, thing.Thing, gow.Wrong}`
  1745  
  1746  	const want = `package main
  1747  
  1748  import (
  1749  	"foo.com/a.thing"
  1750  	"foo.com/go-thing"
  1751  	gow "foo.com/go-wrong"
  1752  	v2 "foo.com/other/v2"
  1753  	"foo.com/other/v3"
  1754  	bar "foo.com/surprise"
  1755  	v1 "foo.com/v1"
  1756  )
  1757  
  1758  var _ = []interface{}{bar.X, v1.Y, a.A, v2.V2, other.V3, thing.Thing, gow.Wrong}
  1759  `
  1760  
  1761  	testConfig{
  1762  		module: packagestest.Module{
  1763  			Name: "foo.com",
  1764  			Files: fm{
  1765  				"a.thing/a.go":  "package a \n const A = 1",
  1766  				"surprise/x.go": "package bar \n const X = 1",
  1767  				"v1/x.go":       "package v1 \n const Y = 1",
  1768  				"other/v2/y.go": "package v2 \n const V2 = 1",
  1769  				"other/v3/z.go": "package other \n const V3 = 1",
  1770  				"go-thing/b.go": "package thing \n const Thing = 1",
  1771  				"go-wrong/b.go": "package gow \n const Wrong = 1",
  1772  				"test/t.go":     input,
  1773  			},
  1774  		},
  1775  	}.processTest(t, "foo.com", "test/t.go", nil, nil, want)
  1776  }
  1777  
  1778  // Tests that the LocalPrefix option causes imports
  1779  // to be added into a later group (num=3).
  1780  func TestLocalPrefix(t *testing.T) {
  1781  	tests := []struct {
  1782  		name        string
  1783  		modules     []packagestest.Module
  1784  		localPrefix string
  1785  		src         string
  1786  		want        string
  1787  	}{
  1788  		{
  1789  			name: "one_local",
  1790  			modules: []packagestest.Module{
  1791  				{
  1792  					Name: "foo.com",
  1793  					Files: fm{
  1794  						"bar/bar.go": "package bar \n const X = 1",
  1795  					},
  1796  				},
  1797  			},
  1798  			localPrefix: "foo.com/",
  1799  			src:         "package main \n const Y = bar.X \n const _ = runtime.GOOS",
  1800  			want: `package main
  1801  
  1802  import (
  1803  	"runtime"
  1804  
  1805  	"foo.com/bar"
  1806  )
  1807  
  1808  const Y = bar.X
  1809  const _ = runtime.GOOS
  1810  `,
  1811  		},
  1812  		{
  1813  			name: "two_local",
  1814  			modules: []packagestest.Module{
  1815  				{
  1816  					Name: "foo.com",
  1817  					Files: fm{
  1818  						"foo/foo.go":     "package foo \n const X = 1",
  1819  						"foo/bar/bar.go": "package bar \n const X = 1",
  1820  					},
  1821  				},
  1822  			},
  1823  			localPrefix: "foo.com/foo",
  1824  			src:         "package main \n const Y = bar.X \n const Z = foo.X \n const _ = runtime.GOOS",
  1825  			want: `package main
  1826  
  1827  import (
  1828  	"runtime"
  1829  
  1830  	"foo.com/foo"
  1831  	"foo.com/foo/bar"
  1832  )
  1833  
  1834  const Y = bar.X
  1835  const Z = foo.X
  1836  const _ = runtime.GOOS
  1837  `,
  1838  		},
  1839  		{
  1840  			name: "three_prefixes",
  1841  			modules: []packagestest.Module{
  1842  				{
  1843  					Name:  "example.org/pkg",
  1844  					Files: fm{"pkg.go": "package pkg \n const A = 1"},
  1845  				},
  1846  				{
  1847  					Name:  "foo.com",
  1848  					Files: fm{"bar/bar.go": "package bar \n const B = 1"},
  1849  				},
  1850  				{
  1851  					Name:  "code.org/r/p",
  1852  					Files: fm{"expproj/expproj.go": "package expproj \n const C = 1"},
  1853  				},
  1854  			},
  1855  			localPrefix: "example.org/pkg,foo.com/,code.org",
  1856  			src:         "package main \n const X = pkg.A \n const Y = bar.B \n const Z = expproj.C \n const _ = runtime.GOOS",
  1857  			want: `package main
  1858  
  1859  import (
  1860  	"runtime"
  1861  
  1862  	"code.org/r/p/expproj"
  1863  	"example.org/pkg"
  1864  	"foo.com/bar"
  1865  )
  1866  
  1867  const X = pkg.A
  1868  const Y = bar.B
  1869  const Z = expproj.C
  1870  const _ = runtime.GOOS
  1871  `,
  1872  		},
  1873  	}
  1874  
  1875  	for _, tt := range tests {
  1876  		t.Run(tt.name, func(t *testing.T) {
  1877  			testConfig{
  1878  				// The module being processed has to be first so it's the primary module.
  1879  				modules: append([]packagestest.Module{{
  1880  					Name:  "test.com",
  1881  					Files: fm{"t.go": tt.src},
  1882  				}}, tt.modules...),
  1883  			}.test(t, func(t *goimportTest) {
  1884  				options := &Options{
  1885  					LocalPrefix: tt.localPrefix,
  1886  					TabWidth:    8,
  1887  					TabIndent:   true,
  1888  					Comments:    true,
  1889  					Fragment:    true,
  1890  				}
  1891  				t.assertProcessEquals("test.com", "t.go", nil, options, tt.want)
  1892  			})
  1893  		})
  1894  	}
  1895  }
  1896  
  1897  // Tests that "package documentation" files are ignored.
  1898  func TestIgnoreDocumentationPackage(t *testing.T) {
  1899  	const input = `package x
  1900  
  1901  const Y = foo.X
  1902  `
  1903  	const want = `package x
  1904  
  1905  import "foo.com/foo"
  1906  
  1907  const Y = foo.X
  1908  `
  1909  
  1910  	testConfig{
  1911  		module: packagestest.Module{
  1912  			Name: "foo.com",
  1913  			Files: fm{
  1914  				"foo/foo.go": "package foo\nconst X = 1\n",
  1915  				"foo/doc.go": "package documentation \n // just to confuse things\n",
  1916  				"x/x.go":     input,
  1917  			},
  1918  		},
  1919  	}.processTest(t, "foo.com", "x/x.go", nil, nil, want)
  1920  }
  1921  
  1922  // Tests importPathToNameGoPathParse and in particular that it stops
  1923  // after finding the first non-documentation package name, not
  1924  // reporting an error on inconsistent package names (since it should
  1925  // never make it that far).
  1926  func TestImportPathToNameGoPathParse(t *testing.T) {
  1927  	testConfig{
  1928  		module: packagestest.Module{
  1929  			Name: "example.net/pkg",
  1930  			Files: fm{
  1931  				"doc.go": "package documentation\n", // ignored
  1932  				"gen.go": "package main\n",          // also ignored
  1933  				"pkg.go": "package the_pkg_name_to_find\n  and this syntax error is ignored because of parser.PackageClauseOnly",
  1934  				"z.go":   "package inconsistent\n", // inconsistent but ignored
  1935  			},
  1936  		},
  1937  	}.test(t, func(t *goimportTest) {
  1938  		if strings.Contains(t.Name(), "GoPackages") {
  1939  			t.Skip("go/packages does not ignore package main")
  1940  		}
  1941  		r, err := t.env.GetResolver()
  1942  		if err != nil {
  1943  			t.Fatal(err)
  1944  		}
  1945  		srcDir := filepath.Dir(t.exported.File("example.net/pkg", "z.go"))
  1946  		names, err := r.loadPackageNames([]string{"example.net/pkg"}, srcDir)
  1947  		if err != nil {
  1948  			t.Fatal(err)
  1949  		}
  1950  		const want = "the_pkg_name_to_find"
  1951  		if got := names["example.net/pkg"]; got != want {
  1952  			t.Errorf("loadPackageNames(..) = %q; want %q", got, want)
  1953  		}
  1954  	})
  1955  }
  1956  
  1957  func TestIgnoreConfiguration(t *testing.T) {
  1958  	const input = `package x
  1959  
  1960  const _ = pkg.X
  1961  `
  1962  	const want = `package x
  1963  
  1964  import "foo.com/otherwise-longer-so-worse-example/foo/pkg"
  1965  
  1966  const _ = pkg.X
  1967  `
  1968  
  1969  	testConfig{
  1970  		gopathOnly: true,
  1971  		module: packagestest.Module{
  1972  			Name: "foo.com",
  1973  			Files: fm{
  1974  				"../.goimportsignore":                              "# comment line\n\n foo.com/example", // tests comment, blank line, whitespace trimming
  1975  				"example/pkg/pkg.go":                               "package pkg\nconst X = 1",
  1976  				"otherwise-longer-so-worse-example/foo/pkg/pkg.go": "package pkg\nconst X = 1",
  1977  				"x/x.go": input,
  1978  			},
  1979  		},
  1980  	}.processTest(t, "foo.com", "x/x.go", nil, nil, want)
  1981  }
  1982  
  1983  // Skip "node_modules" directory.
  1984  func TestSkipNodeModules(t *testing.T) {
  1985  	const input = `package x
  1986  
  1987  const _ = pkg.X
  1988  `
  1989  	const want = `package x
  1990  
  1991  import "foo.com/otherwise-longer/not_modules/pkg"
  1992  
  1993  const _ = pkg.X
  1994  `
  1995  
  1996  	testConfig{
  1997  		gopathOnly: true,
  1998  		module: packagestest.Module{
  1999  			Name: "foo.com",
  2000  			Files: fm{
  2001  				"example/node_modules/pkg/a.go":         "package pkg\nconst X = 1",
  2002  				"otherwise-longer/not_modules/pkg/a.go": "package pkg\nconst X = 1",
  2003  				"x/x.go":                                input,
  2004  			},
  2005  		},
  2006  	}.processTest(t, "foo.com", "x/x.go", nil, nil, want)
  2007  }
  2008  
  2009  // Tests that package global variables with the same name and function name as
  2010  // a function in a separate package do not result in an import which masks
  2011  // the global variable
  2012  func TestGlobalImports(t *testing.T) {
  2013  	const usesGlobal = `package pkg
  2014  
  2015  func doSomething() {
  2016  	t := time.Now()
  2017  }
  2018  `
  2019  
  2020  	const declaresGlobal = `package pkg
  2021  
  2022  type Time struct{}
  2023  
  2024  func (t Time) Now() Time {
  2025  	return Time{}
  2026  }
  2027  
  2028  var time Time
  2029  `
  2030  
  2031  	testConfig{
  2032  		module: packagestest.Module{
  2033  			Name: "foo.com",
  2034  			Files: fm{
  2035  				"pkg/uses.go":   usesGlobal,
  2036  				"pkg/global.go": declaresGlobal,
  2037  			},
  2038  		},
  2039  	}.processTest(t, "foo.com", "pkg/uses.go", nil, nil, usesGlobal)
  2040  }
  2041  
  2042  // Some people put multiple packages' files in the same directory. Globals
  2043  // declared in other packages should be ignored.
  2044  func TestGlobalImports_DifferentPackage(t *testing.T) {
  2045  	const declaresGlobal = `package main
  2046  var fmt int
  2047  `
  2048  	const input = `package pkg
  2049  var _ = fmt.Printf
  2050  `
  2051  	const want = `package pkg
  2052  
  2053  import "fmt"
  2054  
  2055  var _ = fmt.Printf
  2056  `
  2057  
  2058  	testConfig{
  2059  		module: packagestest.Module{
  2060  			Name: "foo.com",
  2061  			Files: fm{
  2062  				"pkg/main.go": declaresGlobal,
  2063  				"pkg/uses.go": input,
  2064  			},
  2065  		},
  2066  	}.processTest(t, "foo.com", "pkg/uses.go", nil, nil, want)
  2067  }
  2068  
  2069  func TestGlobalImports_MultipleMains(t *testing.T) {
  2070  	const declaresGlobal = `package main
  2071  var fmt int
  2072  `
  2073  	const input = `package main
  2074  import "fmt"
  2075  var _, _ = fmt.Printf, bytes.Equal
  2076  `
  2077  	const want = `package main
  2078  
  2079  import (
  2080  	"bytes"
  2081  	"fmt"
  2082  )
  2083  
  2084  var _, _ = fmt.Printf, bytes.Equal
  2085  `
  2086  
  2087  	testConfig{
  2088  		module: packagestest.Module{
  2089  			Name: "foo.com",
  2090  			Files: fm{
  2091  				"pkg/main.go": declaresGlobal,
  2092  				"pkg/uses.go": input,
  2093  			},
  2094  		},
  2095  	}.processTest(t, "foo.com", "pkg/uses.go", nil, nil, want)
  2096  }
  2097  
  2098  // Tests that sibling files - other files in the same package - can provide an
  2099  // import that may not be the default one otherwise.
  2100  func TestSiblingImports(t *testing.T) {
  2101  
  2102  	// provide is the sibling file that provides the desired import.
  2103  	const provide = `package siblingimporttest
  2104  
  2105  import "local/log"
  2106  import "my/bytes"
  2107  import renamed "fmt"
  2108  
  2109  func LogSomething() {
  2110  	log.Print("Something")
  2111  	bytes.SomeFunc()
  2112  	renamed.Println("Something")
  2113  }
  2114  `
  2115  
  2116  	// need is the file being tested that needs the import.
  2117  	const need = `package siblingimporttest
  2118  
  2119  var _ = bytes.Buffer{}
  2120  
  2121  func LogSomethingElse() {
  2122  	log.Print("Something else")
  2123  	renamed.Println("Yet another")
  2124  }
  2125  `
  2126  
  2127  	// want is the expected result file
  2128  	const want = `package siblingimporttest
  2129  
  2130  import (
  2131  	"bytes"
  2132  	renamed "fmt"
  2133  	"local/log"
  2134  )
  2135  
  2136  var _ = bytes.Buffer{}
  2137  
  2138  func LogSomethingElse() {
  2139  	log.Print("Something else")
  2140  	renamed.Println("Yet another")
  2141  }
  2142  `
  2143  
  2144  	testConfig{
  2145  		module: packagestest.Module{
  2146  			Name: "foo.com",
  2147  			Files: fm{
  2148  				"p/needs_import.go":    need,
  2149  				"p/provides_import.go": provide,
  2150  			},
  2151  		},
  2152  	}.processTest(t, "foo.com", "p/needs_import.go", nil, nil, want)
  2153  }
  2154  
  2155  // Tests #29180: a sibling import of the right package with the wrong name is used.
  2156  func TestSiblingImport_Misnamed(t *testing.T) {
  2157  	const sibling = `package main
  2158  import renamed "fmt"
  2159  var _ = renamed.Printf
  2160  `
  2161  	const input = `package pkg
  2162  var _ = fmt.Printf
  2163  `
  2164  	const want = `package pkg
  2165  
  2166  import "fmt"
  2167  
  2168  var _ = fmt.Printf
  2169  `
  2170  
  2171  	testConfig{
  2172  		module: packagestest.Module{
  2173  			Name: "foo.com",
  2174  			Files: fm{
  2175  				"pkg/main.go": sibling,
  2176  				"pkg/uses.go": input,
  2177  			},
  2178  		},
  2179  	}.processTest(t, "foo.com", "pkg/uses.go", nil, nil, want)
  2180  
  2181  }
  2182  
  2183  // Tests that an input file's own package is ignored.
  2184  func TestIgnoreOwnPackage(t *testing.T) {
  2185  	const input = `package pkg
  2186  
  2187  const _ = pkg.X
  2188  `
  2189  	const want = `package pkg
  2190  
  2191  const _ = pkg.X
  2192  `
  2193  
  2194  	testConfig{
  2195  		module: packagestest.Module{
  2196  			Name: "foo.com",
  2197  			Files: fm{
  2198  				"pkg/a.go": "package pkg\nconst X = 1",
  2199  				"pkg/b.go": input,
  2200  			},
  2201  		},
  2202  	}.processTest(t, "foo.com", "pkg/b.go", nil, nil, want)
  2203  }
  2204  
  2205  func TestExternalTestImportsPackageUnderTest(t *testing.T) {
  2206  	const provide = `package pkg
  2207  func DoIt(){}
  2208  `
  2209  	const input = `package pkg_test
  2210  
  2211  var _ = pkg.DoIt`
  2212  
  2213  	const want = `package pkg_test
  2214  
  2215  import "foo.com/pkg"
  2216  
  2217  var _ = pkg.DoIt
  2218  `
  2219  
  2220  	testConfig{
  2221  		module: packagestest.Module{
  2222  			Name: "foo.com",
  2223  			Files: fm{
  2224  				"pkg/provide.go": provide,
  2225  				"pkg/x_test.go":  input,
  2226  			},
  2227  		},
  2228  	}.processTest(t, "foo.com", "pkg/x_test.go", nil, nil, want)
  2229  }
  2230  
  2231  func TestPkgIsCandidate(t *testing.T) {
  2232  	tests := []struct {
  2233  		name     string
  2234  		filename string
  2235  		pkgIdent string
  2236  		pkg      *pkg
  2237  		want     bool
  2238  	}{
  2239  		{
  2240  			name:     "normal_match",
  2241  			filename: "/gopath/src/my/pkg/pkg.go",
  2242  			pkgIdent: "client",
  2243  			pkg: &pkg{
  2244  				dir:             "/gopath/src/client",
  2245  				importPathShort: "client",
  2246  			},
  2247  			want: true,
  2248  		},
  2249  		{
  2250  			name:     "no_match",
  2251  			filename: "/gopath/src/my/pkg/pkg.go",
  2252  			pkgIdent: "zzz",
  2253  			pkg: &pkg{
  2254  				dir:             "/gopath/src/client",
  2255  				importPathShort: "client",
  2256  			},
  2257  			want: false,
  2258  		},
  2259  		{
  2260  			name:     "match_too_early",
  2261  			filename: "/gopath/src/my/pkg/pkg.go",
  2262  			pkgIdent: "client",
  2263  			pkg: &pkg{
  2264  				dir:             "/gopath/src/client/foo/foo/foo",
  2265  				importPathShort: "client/foo/foo",
  2266  			},
  2267  			want: false,
  2268  		},
  2269  		{
  2270  			name:     "substring_match",
  2271  			filename: "/gopath/src/my/pkg/pkg.go",
  2272  			pkgIdent: "client",
  2273  			pkg: &pkg{
  2274  				dir:             "/gopath/src/foo/go-client",
  2275  				importPathShort: "foo/go-client",
  2276  			},
  2277  			want: true,
  2278  		},
  2279  		{
  2280  			name:     "hidden_internal",
  2281  			filename: "/gopath/src/my/pkg/pkg.go",
  2282  			pkgIdent: "client",
  2283  			pkg: &pkg{
  2284  				dir:             "/gopath/src/foo/internal/client",
  2285  				importPathShort: "foo/internal/client",
  2286  			},
  2287  			want: false,
  2288  		},
  2289  		{
  2290  			name:     "visible_internal",
  2291  			filename: "/gopath/src/foo/bar.go",
  2292  			pkgIdent: "client",
  2293  			pkg: &pkg{
  2294  				dir:             "/gopath/src/foo/internal/client",
  2295  				importPathShort: "foo/internal/client",
  2296  			},
  2297  			want: true,
  2298  		},
  2299  		{
  2300  			name:     "invisible_vendor",
  2301  			filename: "/gopath/src/foo/bar.go",
  2302  			pkgIdent: "client",
  2303  			pkg: &pkg{
  2304  				dir:             "/gopath/src/other/vendor/client",
  2305  				importPathShort: "client",
  2306  			},
  2307  			want: false,
  2308  		},
  2309  		{
  2310  			name:     "visible_vendor",
  2311  			filename: "/gopath/src/foo/bar.go",
  2312  			pkgIdent: "client",
  2313  			pkg: &pkg{
  2314  				dir:             "/gopath/src/foo/vendor/client",
  2315  				importPathShort: "client",
  2316  			},
  2317  			want: true,
  2318  		},
  2319  		{
  2320  			name:     "match_with_hyphens",
  2321  			filename: "/gopath/src/foo/bar.go",
  2322  			pkgIdent: "socketio",
  2323  			pkg: &pkg{
  2324  				dir:             "/gopath/src/foo/socket-io",
  2325  				importPathShort: "foo/socket-io",
  2326  			},
  2327  			want: true,
  2328  		},
  2329  		{
  2330  			name:     "match_with_mixed_case",
  2331  			filename: "/gopath/src/foo/bar.go",
  2332  			pkgIdent: "fooprod",
  2333  			pkg: &pkg{
  2334  				dir:             "/gopath/src/foo/FooPROD",
  2335  				importPathShort: "foo/FooPROD",
  2336  			},
  2337  			want: true,
  2338  		},
  2339  		{
  2340  			name:     "matches_with_hyphen_and_caps",
  2341  			filename: "/gopath/src/foo/bar.go",
  2342  			pkgIdent: "fooprod",
  2343  			pkg: &pkg{
  2344  				dir:             "/gopath/src/foo/Foo-PROD",
  2345  				importPathShort: "foo/Foo-PROD",
  2346  			},
  2347  			want: true,
  2348  		},
  2349  	}
  2350  	for i, tt := range tests {
  2351  		t.Run(tt.name, func(t *testing.T) {
  2352  			refs := references{tt.pkgIdent: nil}
  2353  			got := pkgIsCandidate(tt.filename, refs, tt.pkg)
  2354  			if got != tt.want {
  2355  				t.Errorf("test %d. pkgIsCandidate(%q, %q, %+v) = %v; want %v",
  2356  					i, tt.filename, tt.pkgIdent, *tt.pkg, got, tt.want)
  2357  			}
  2358  		})
  2359  	}
  2360  }
  2361  
  2362  // Issue 20941: this used to panic on Windows.
  2363  func TestProcessStdin(t *testing.T) {
  2364  	testConfig{
  2365  		module: packagestest.Module{
  2366  			Name: "foo.com",
  2367  		},
  2368  	}.test(t, func(t *goimportTest) {
  2369  		got, err := t.processNonModule("<standard input>", []byte("package main\nfunc main() {\n\tfmt.Println(123)\n}\n"), nil)
  2370  		if err != nil {
  2371  			t.Fatal(err)
  2372  		}
  2373  		if !strings.Contains(string(got), `"fmt"`) {
  2374  			t.Errorf("expected fmt import; got: %s", got)
  2375  		}
  2376  	})
  2377  }
  2378  
  2379  // Tests LocalPackagePromotion when there is a local package that matches, it
  2380  // should be the closest match.
  2381  // https://golang.org/issues/17557
  2382  func TestLocalPackagePromotion(t *testing.T) {
  2383  	const input = `package main
  2384  var c = &config.SystemConfig{}
  2385  `
  2386  	const want = `package main
  2387  
  2388  import "mycompany.net/tool/config"
  2389  
  2390  var c = &config.SystemConfig{}
  2391  `
  2392  
  2393  	testConfig{
  2394  		modules: []packagestest.Module{
  2395  			{
  2396  				Name:  "config.net/config",
  2397  				Files: fm{"config.go": "package config\n type SystemConfig struct {}"}, // Will match but should not be first choice
  2398  			},
  2399  			{
  2400  				Name:  "mycompany.net/config",
  2401  				Files: fm{"config.go": "package config\n type SystemConfig struct {}"}, // Will match but should not be first choice
  2402  			},
  2403  			{
  2404  				Name: "mycompany.net/tool",
  2405  				Files: fm{
  2406  					"config/config.go": "package config\n type SystemConfig struct {}", // Local package should be promoted over shorter package
  2407  					"main.go":          input,
  2408  				},
  2409  			},
  2410  		},
  2411  	}.processTest(t, "mycompany.net/tool", "main.go", nil, nil, want)
  2412  }
  2413  
  2414  // Tests FindImportInLocalGoFiles looks at the import lines for other Go files in the
  2415  // local directory, since the user is likely to import the same packages in the current
  2416  // Go file.  If an import is found that satisfies the need, it should be used over the
  2417  // standard library.
  2418  // https://golang.org/issues/17557
  2419  func TestFindImportInLocalGoFiles(t *testing.T) {
  2420  	const input = `package main
  2421   var _ = &bytes.Buffer{}`
  2422  
  2423  	const want = `package main
  2424  
  2425  import "bytes.net/bytes"
  2426  
  2427  var _ = &bytes.Buffer{}
  2428  `
  2429  	testConfig{
  2430  		modules: []packagestest.Module{
  2431  			{
  2432  				Name: "mycompany.net/tool",
  2433  				Files: fm{
  2434  					"io.go":   "package main\n import \"bytes.net/bytes\"\n var _ = &bytes.Buffer{}", // Contains package import that will cause stdlib to be ignored
  2435  					"main.go": input,
  2436  				},
  2437  			},
  2438  			{
  2439  				Name:  "bytes.net/bytes",
  2440  				Files: fm{"bytes.go": "package bytes\n type Buffer struct {}"}, // Should be selected over standard library
  2441  			},
  2442  		},
  2443  	}.processTest(t, "mycompany.net/tool", "main.go", nil, nil, want)
  2444  }
  2445  
  2446  func TestInMemoryFile(t *testing.T) {
  2447  	const input = `package main
  2448   var _ = &bytes.Buffer{}`
  2449  
  2450  	const want = `package main
  2451  
  2452  import "bytes"
  2453  
  2454  var _ = &bytes.Buffer{}
  2455  `
  2456  	testConfig{
  2457  		module: packagestest.Module{
  2458  			Name:  "foo.com",
  2459  			Files: fm{"x.go": "package x\n"},
  2460  		},
  2461  	}.processTest(t, "foo.com", "x.go", []byte(input), nil, want)
  2462  }
  2463  
  2464  func TestImportNoGoFiles(t *testing.T) {
  2465  	const input = `package main
  2466   var _ = &bytes.Buffer{}`
  2467  
  2468  	const want = `package main
  2469  
  2470  import "bytes"
  2471  
  2472  var _ = &bytes.Buffer{}
  2473  `
  2474  	testConfig{
  2475  		module: packagestest.Module{
  2476  			Name: "mycompany.net",
  2477  		},
  2478  	}.test(t, func(t *goimportTest) {
  2479  		buf, err := t.processNonModule("mycompany.net/tool/main.go", []byte(input), nil)
  2480  		if err != nil {
  2481  			t.Fatalf("Process() = %v", err)
  2482  		}
  2483  		if string(buf) != want {
  2484  			t.Errorf("Got:\n%s\nWant:\n%s", buf, want)
  2485  		}
  2486  	})
  2487  
  2488  }
  2489  
  2490  // Ensures a token as large as 500000 bytes can be handled
  2491  // https://golang.org/issues/18201
  2492  func TestProcessLargeToken(t *testing.T) {
  2493  	largeString := strings.Repeat("x", 500000)
  2494  
  2495  	input := `package testimports
  2496  
  2497  import (
  2498  	"bytes"
  2499  )
  2500  
  2501  const s = fmt.Sprintf("%s", "` + largeString + `")
  2502  var _ = bytes.Buffer{}
  2503  
  2504  // end
  2505  `
  2506  
  2507  	want := `package testimports
  2508  
  2509  import (
  2510  	"bytes"
  2511  	"fmt"
  2512  )
  2513  
  2514  const s = fmt.Sprintf("%s", "` + largeString + `")
  2515  
  2516  var _ = bytes.Buffer{}
  2517  
  2518  // end
  2519  `
  2520  
  2521  	testConfig{
  2522  		module: packagestest.Module{
  2523  			Name:  "foo.com",
  2524  			Files: fm{"foo.go": input},
  2525  		},
  2526  	}.processTest(t, "foo.com", "foo.go", nil, nil, want)
  2527  }
  2528  
  2529  // Tests that an external test package will import the package under test if it
  2530  // also uses symbols exported only in test files.
  2531  // https://golang.org/issues/29979
  2532  func TestExternalTest(t *testing.T) {
  2533  	const input = `package a_test
  2534  func TestX() {
  2535  	a.X()
  2536  	a.Y()
  2537  }
  2538  `
  2539  	const want = `package a_test
  2540  
  2541  import "foo.com/a"
  2542  
  2543  func TestX() {
  2544  	a.X()
  2545  	a.Y()
  2546  }
  2547  `
  2548  
  2549  	testConfig{
  2550  		modules: []packagestest.Module{
  2551  			{
  2552  				Name: "foo.com/a",
  2553  				Files: fm{
  2554  					"a.go":           "package a\n func X() {}",
  2555  					"export_test.go": "package a\n func Y() {}",
  2556  					"a_test.go":      input,
  2557  				},
  2558  			},
  2559  		},
  2560  	}.processTest(t, "foo.com/a", "a_test.go", nil, nil, want)
  2561  }
  2562  
  2563  // TestGetCandidates tests that get packages finds packages
  2564  // with correct priorities.
  2565  func TestGetCandidates(t *testing.T) {
  2566  	type res struct {
  2567  		relevance  float64
  2568  		name, path string
  2569  	}
  2570  	want := []res{
  2571  		{0, "bytes", "bytes"},
  2572  		{0, "http", "net/http"},
  2573  		{0, "rand", "crypto/rand"},
  2574  		{0, "bar", "bar.com/bar"},
  2575  		{0, "foo", "foo.com/foo"},
  2576  	}
  2577  
  2578  	testConfig{
  2579  		modules: []packagestest.Module{
  2580  			{
  2581  				Name:  "bar.com",
  2582  				Files: fm{"bar/bar.go": "package bar\n"},
  2583  			},
  2584  			{
  2585  				Name:  "foo.com",
  2586  				Files: fm{"foo/foo.go": "package foo\n"},
  2587  			},
  2588  		},
  2589  	}.test(t, func(t *goimportTest) {
  2590  		var mu sync.Mutex
  2591  		var got []res
  2592  		add := func(c ImportFix) {
  2593  			mu.Lock()
  2594  			defer mu.Unlock()
  2595  			for _, w := range want {
  2596  				if c.StmtInfo.ImportPath == w.path {
  2597  					got = append(got, res{c.Relevance, c.IdentName, c.StmtInfo.ImportPath})
  2598  				}
  2599  			}
  2600  		}
  2601  		if err := GetAllCandidates(context.Background(), add, "", "x.go", "x", t.env); err != nil {
  2602  			t.Fatalf("GetAllCandidates() = %v", err)
  2603  		}
  2604  		// Sort, then clear out relevance so it doesn't mess up the DeepEqual.
  2605  		sort.Slice(got, func(i, j int) bool {
  2606  			ri, rj := got[i], got[j]
  2607  			if ri.relevance != rj.relevance {
  2608  				return ri.relevance > rj.relevance // Highest first.
  2609  			}
  2610  			return ri.name < rj.name
  2611  		})
  2612  		for i := range got {
  2613  			got[i].relevance = 0
  2614  		}
  2615  		if !reflect.DeepEqual(want, got) {
  2616  			t.Errorf("wanted results in order %v, got %v", want, got)
  2617  		}
  2618  	})
  2619  }
  2620  
  2621  func TestGetImportPaths(t *testing.T) {
  2622  	type res struct {
  2623  		relevance  float64
  2624  		name, path string
  2625  	}
  2626  	want := []res{
  2627  		{0, "http", "net/http"},
  2628  		{0, "net", "net"},
  2629  		{0, "neta", "neta.com/neta"},
  2630  	}
  2631  
  2632  	testConfig{
  2633  		modules: []packagestest.Module{
  2634  			{
  2635  				Name:  "neta.com",
  2636  				Files: fm{"neta/neta.go": "package neta\n"},
  2637  			},
  2638  		},
  2639  	}.test(t, func(t *goimportTest) {
  2640  		var mu sync.Mutex
  2641  		var got []res
  2642  		add := func(c ImportFix) {
  2643  			mu.Lock()
  2644  			defer mu.Unlock()
  2645  			for _, w := range want {
  2646  				if c.StmtInfo.ImportPath == w.path {
  2647  					got = append(got, res{c.Relevance, c.IdentName, c.StmtInfo.ImportPath})
  2648  				}
  2649  			}
  2650  		}
  2651  		if err := GetImportPaths(context.Background(), add, "ne", "x.go", "x", t.env); err != nil {
  2652  			t.Fatalf("GetImportPaths() = %v", err)
  2653  		}
  2654  		// Sort, then clear out relevance so it doesn't mess up the DeepEqual.
  2655  		sort.Slice(got, func(i, j int) bool {
  2656  			ri, rj := got[i], got[j]
  2657  			if ri.relevance != rj.relevance {
  2658  				return ri.relevance > rj.relevance // Highest first.
  2659  			}
  2660  			return ri.name < rj.name
  2661  		})
  2662  		for i := range got {
  2663  			got[i].relevance = 0
  2664  		}
  2665  		if !reflect.DeepEqual(want, got) {
  2666  			t.Errorf("wanted results in order %v, got %v", want, got)
  2667  		}
  2668  	})
  2669  }
  2670  
  2671  func TestGetPackageCompletions(t *testing.T) {
  2672  	type res struct {
  2673  		relevance          float64
  2674  		name, path, symbol string
  2675  	}
  2676  	want := []res{
  2677  		{0, "rand", "math/rand", "Seed"},
  2678  		{0, "rand", "bar.com/rand", "Bar"},
  2679  	}
  2680  
  2681  	testConfig{
  2682  		modules: []packagestest.Module{
  2683  			{
  2684  				Name:  "bar.com",
  2685  				Files: fm{"rand/bar.go": "package rand\nvar Bar int\n"},
  2686  			},
  2687  		},
  2688  	}.test(t, func(t *goimportTest) {
  2689  		var mu sync.Mutex
  2690  		var got []res
  2691  		add := func(c PackageExport) {
  2692  			mu.Lock()
  2693  			defer mu.Unlock()
  2694  			for _, csym := range c.Exports {
  2695  				for _, w := range want {
  2696  					if c.Fix.StmtInfo.ImportPath == w.path && csym == w.symbol {
  2697  						got = append(got, res{c.Fix.Relevance, c.Fix.IdentName, c.Fix.StmtInfo.ImportPath, csym})
  2698  					}
  2699  				}
  2700  			}
  2701  		}
  2702  		if err := GetPackageExports(context.Background(), add, "rand", "x.go", "x", t.env); err != nil {
  2703  			t.Fatalf("getPackageCompletions() = %v", err)
  2704  		}
  2705  		// Sort, then clear out relevance so it doesn't mess up the DeepEqual.
  2706  		sort.Slice(got, func(i, j int) bool {
  2707  			ri, rj := got[i], got[j]
  2708  			if ri.relevance != rj.relevance {
  2709  				return ri.relevance > rj.relevance // Highest first.
  2710  			}
  2711  			return ri.name < rj.name
  2712  		})
  2713  		for i := range got {
  2714  			got[i].relevance = 0
  2715  		}
  2716  		if !reflect.DeepEqual(want, got) {
  2717  			t.Errorf("wanted results in order %v, got %v", want, got)
  2718  		}
  2719  	})
  2720  }
  2721  
  2722  // Tests #34895: process should not panic on concurrent calls.
  2723  func TestConcurrentProcess(t *testing.T) {
  2724  	testConfig{
  2725  		module: packagestest.Module{
  2726  			Name: "foo.com",
  2727  			Files: fm{
  2728  				"p/first.go": `package foo
  2729  
  2730  func _() {
  2731  	fmt.Println()
  2732  }
  2733  `,
  2734  				"p/second.go": `package foo
  2735  
  2736  import "fmt"
  2737  
  2738  func _() {
  2739  	fmt.Println()
  2740  	imports.Bar() // not imported.
  2741  }
  2742  `,
  2743  			},
  2744  		},
  2745  	}.test(t, func(t *goimportTest) {
  2746  		var (
  2747  			n  = 10
  2748  			wg sync.WaitGroup
  2749  		)
  2750  		wg.Add(n)
  2751  		for i := 0; i < n; i++ {
  2752  			go func() {
  2753  				defer wg.Done()
  2754  				_, err := t.process("foo.com", "p/first.go", nil, nil)
  2755  				if err != nil {
  2756  					t.Error(err)
  2757  				}
  2758  			}()
  2759  		}
  2760  		wg.Wait()
  2761  	})
  2762  }
  2763  
  2764  func TestNonlocalDot(t *testing.T) {
  2765  	const input = `package main
  2766  import (
  2767  	"fmt"
  2768  )
  2769  var _, _ = fmt.Sprintf, dot.Dot
  2770  `
  2771  	const want = `package main
  2772  
  2773  import (
  2774  	"fmt"
  2775  	"noninternet/dot.v1/dot"
  2776  )
  2777  
  2778  var _, _ = fmt.Sprintf, dot.Dot
  2779  `
  2780  	testConfig{
  2781  		modules: []packagestest.Module{
  2782  			{
  2783  				Name:  "golang.org/fake",
  2784  				Files: fm{"x.go": input},
  2785  			},
  2786  			{
  2787  				Name: "noninternet/dot.v1",
  2788  				Files: fm{
  2789  					"dot/dot.go": "package dot\nfunc Dot(){}\n",
  2790  				},
  2791  			},
  2792  		},
  2793  		gopathOnly: true, // our modules testing setup doesn't allow modules without dots.
  2794  	}.processTest(t, "golang.org/fake", "x.go", nil, nil, want)
  2795  }