Source file src/go/build/build_test.go

     1  // Copyright 2011 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 build
     6  
     7  import (
     8  	"internal/testenv"
     9  	"io"
    10  	"os"
    11  	"path/filepath"
    12  	"reflect"
    13  	"runtime"
    14  	"strings"
    15  	"testing"
    16  )
    17  
    18  func TestMain(m *testing.M) {
    19  	Default.GOROOT = testenv.GOROOT(nil)
    20  	os.Exit(m.Run())
    21  }
    22  
    23  func TestMatch(t *testing.T) {
    24  	ctxt := Default
    25  	what := "default"
    26  	match := func(tag string, want map[string]bool) {
    27  		t.Helper()
    28  		m := make(map[string]bool)
    29  		if !ctxt.matchAuto(tag, m) {
    30  			t.Errorf("%s context should match %s, does not", what, tag)
    31  		}
    32  		if !reflect.DeepEqual(m, want) {
    33  			t.Errorf("%s tags = %v, want %v", tag, m, want)
    34  		}
    35  	}
    36  	nomatch := func(tag string, want map[string]bool) {
    37  		t.Helper()
    38  		m := make(map[string]bool)
    39  		if ctxt.matchAuto(tag, m) {
    40  			t.Errorf("%s context should NOT match %s, does", what, tag)
    41  		}
    42  		if !reflect.DeepEqual(m, want) {
    43  			t.Errorf("%s tags = %v, want %v", tag, m, want)
    44  		}
    45  	}
    46  
    47  	match(runtime.GOOS+","+runtime.GOARCH, map[string]bool{runtime.GOOS: true, runtime.GOARCH: true})
    48  	match(runtime.GOOS+","+runtime.GOARCH+",!foo", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "foo": true})
    49  	nomatch(runtime.GOOS+","+runtime.GOARCH+",foo", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "foo": true})
    50  
    51  	what = "modified"
    52  	ctxt.BuildTags = []string{"foo"}
    53  	match(runtime.GOOS+","+runtime.GOARCH, map[string]bool{runtime.GOOS: true, runtime.GOARCH: true})
    54  	match(runtime.GOOS+","+runtime.GOARCH+",foo", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "foo": true})
    55  	nomatch(runtime.GOOS+","+runtime.GOARCH+",!foo", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "foo": true})
    56  	match(runtime.GOOS+","+runtime.GOARCH+",!bar", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "bar": true})
    57  	nomatch(runtime.GOOS+","+runtime.GOARCH+",bar", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "bar": true})
    58  }
    59  
    60  func TestDotSlashImport(t *testing.T) {
    61  	p, err := ImportDir("testdata/other", 0)
    62  	if err != nil {
    63  		t.Fatal(err)
    64  	}
    65  	if len(p.Imports) != 1 || p.Imports[0] != "./file" {
    66  		t.Fatalf("testdata/other: Imports=%v, want [./file]", p.Imports)
    67  	}
    68  
    69  	p1, err := Import("./file", "testdata/other", 0)
    70  	if err != nil {
    71  		t.Fatal(err)
    72  	}
    73  	if p1.Name != "file" {
    74  		t.Fatalf("./file: Name=%q, want %q", p1.Name, "file")
    75  	}
    76  	dir := filepath.Clean("testdata/other/file") // Clean to use \ on Windows
    77  	if p1.Dir != dir {
    78  		t.Fatalf("./file: Dir=%q, want %q", p1.Name, dir)
    79  	}
    80  }
    81  
    82  func TestEmptyImport(t *testing.T) {
    83  	p, err := Import("", testenv.GOROOT(t), FindOnly)
    84  	if err == nil {
    85  		t.Fatal(`Import("") returned nil error.`)
    86  	}
    87  	if p == nil {
    88  		t.Fatal(`Import("") returned nil package.`)
    89  	}
    90  	if p.ImportPath != "" {
    91  		t.Fatalf("ImportPath=%q, want %q.", p.ImportPath, "")
    92  	}
    93  }
    94  
    95  func TestEmptyFolderImport(t *testing.T) {
    96  	_, err := Import(".", "testdata/empty", 0)
    97  	if _, ok := err.(*NoGoError); !ok {
    98  		t.Fatal(`Import("testdata/empty") did not return NoGoError.`)
    99  	}
   100  }
   101  
   102  func TestMultiplePackageImport(t *testing.T) {
   103  	pkg, err := Import(".", "testdata/multi", 0)
   104  
   105  	mpe, ok := err.(*MultiplePackageError)
   106  	if !ok {
   107  		t.Fatal(`Import("testdata/multi") did not return MultiplePackageError.`)
   108  	}
   109  	want := &MultiplePackageError{
   110  		Dir:      filepath.FromSlash("testdata/multi"),
   111  		Packages: []string{"main", "test_package"},
   112  		Files:    []string{"file.go", "file_appengine.go"},
   113  	}
   114  	if !reflect.DeepEqual(mpe, want) {
   115  		t.Errorf("err = %#v; want %#v", mpe, want)
   116  	}
   117  
   118  	// TODO(#45999): Since the name is ambiguous, pkg.Name should be left empty.
   119  	if wantName := "main"; pkg.Name != wantName {
   120  		t.Errorf("pkg.Name = %q; want %q", pkg.Name, wantName)
   121  	}
   122  
   123  	if wantGoFiles := []string{"file.go", "file_appengine.go"}; !reflect.DeepEqual(pkg.GoFiles, wantGoFiles) {
   124  		t.Errorf("pkg.GoFiles = %q; want %q", pkg.GoFiles, wantGoFiles)
   125  	}
   126  
   127  	if wantInvalidFiles := []string{"file_appengine.go"}; !reflect.DeepEqual(pkg.InvalidGoFiles, wantInvalidFiles) {
   128  		t.Errorf("pkg.InvalidGoFiles = %q; want %q", pkg.InvalidGoFiles, wantInvalidFiles)
   129  	}
   130  }
   131  
   132  func TestLocalDirectory(t *testing.T) {
   133  	if runtime.GOOS == "ios" {
   134  		t.Skipf("skipping on %s/%s, no valid GOROOT", runtime.GOOS, runtime.GOARCH)
   135  	}
   136  
   137  	cwd, err := os.Getwd()
   138  	if err != nil {
   139  		t.Fatal(err)
   140  	}
   141  
   142  	p, err := ImportDir(cwd, 0)
   143  	if err != nil {
   144  		t.Fatal(err)
   145  	}
   146  	if p.ImportPath != "go/build" {
   147  		t.Fatalf("ImportPath=%q, want %q", p.ImportPath, "go/build")
   148  	}
   149  }
   150  
   151  var shouldBuildTests = []struct {
   152  	name        string
   153  	content     string
   154  	tags        map[string]bool
   155  	binaryOnly  bool
   156  	shouldBuild bool
   157  	err         error
   158  }{
   159  	{
   160  		name: "Yes",
   161  		content: "// +build yes\n\n" +
   162  			"package main\n",
   163  		tags:        map[string]bool{"yes": true},
   164  		shouldBuild: true,
   165  	},
   166  	{
   167  		name: "Yes2",
   168  		content: "//go:build yes\n" +
   169  			"package main\n",
   170  		tags:        map[string]bool{"yes": true},
   171  		shouldBuild: true,
   172  	},
   173  	{
   174  		name: "Or",
   175  		content: "// +build no yes\n\n" +
   176  			"package main\n",
   177  		tags:        map[string]bool{"yes": true, "no": true},
   178  		shouldBuild: true,
   179  	},
   180  	{
   181  		name: "Or2",
   182  		content: "//go:build no || yes\n" +
   183  			"package main\n",
   184  		tags:        map[string]bool{"yes": true, "no": true},
   185  		shouldBuild: true,
   186  	},
   187  	{
   188  		name: "And",
   189  		content: "// +build no,yes\n\n" +
   190  			"package main\n",
   191  		tags:        map[string]bool{"yes": true, "no": true},
   192  		shouldBuild: false,
   193  	},
   194  	{
   195  		name: "And2",
   196  		content: "//go:build no && yes\n" +
   197  			"package main\n",
   198  		tags:        map[string]bool{"yes": true, "no": true},
   199  		shouldBuild: false,
   200  	},
   201  	{
   202  		name: "Cgo",
   203  		content: "// +build cgo\n\n" +
   204  			"// Copyright The Go Authors.\n\n" +
   205  			"// This package implements parsing of tags like\n" +
   206  			"// +build tag1\n" +
   207  			"package build",
   208  		tags:        map[string]bool{"cgo": true},
   209  		shouldBuild: false,
   210  	},
   211  	{
   212  		name: "Cgo2",
   213  		content: "//go:build cgo\n" +
   214  			"// Copyright The Go Authors.\n\n" +
   215  			"// This package implements parsing of tags like\n" +
   216  			"// +build tag1\n" +
   217  			"package build",
   218  		tags:        map[string]bool{"cgo": true},
   219  		shouldBuild: false,
   220  	},
   221  	{
   222  		name: "AfterPackage",
   223  		content: "// Copyright The Go Authors.\n\n" +
   224  			"package build\n\n" +
   225  			"// shouldBuild checks tags given by lines of the form\n" +
   226  			"// +build tag\n" +
   227  			"//go:build tag\n" +
   228  			"func shouldBuild(content []byte)\n",
   229  		tags:        map[string]bool{},
   230  		shouldBuild: true,
   231  	},
   232  	{
   233  		name: "TooClose",
   234  		content: "// +build yes\n" +
   235  			"package main\n",
   236  		tags:        map[string]bool{},
   237  		shouldBuild: true,
   238  	},
   239  	{
   240  		name: "TooClose2",
   241  		content: "//go:build yes\n" +
   242  			"package main\n",
   243  		tags:        map[string]bool{"yes": true},
   244  		shouldBuild: true,
   245  	},
   246  	{
   247  		name: "TooCloseNo",
   248  		content: "// +build no\n" +
   249  			"package main\n",
   250  		tags:        map[string]bool{},
   251  		shouldBuild: true,
   252  	},
   253  	{
   254  		name: "TooCloseNo2",
   255  		content: "//go:build no\n" +
   256  			"package main\n",
   257  		tags:        map[string]bool{"no": true},
   258  		shouldBuild: false,
   259  	},
   260  	{
   261  		name: "BinaryOnly",
   262  		content: "//go:binary-only-package\n" +
   263  			"// +build yes\n" +
   264  			"package main\n",
   265  		tags:        map[string]bool{},
   266  		binaryOnly:  true,
   267  		shouldBuild: true,
   268  	},
   269  	{
   270  		name: "BinaryOnly2",
   271  		content: "//go:binary-only-package\n" +
   272  			"//go:build no\n" +
   273  			"package main\n",
   274  		tags:        map[string]bool{"no": true},
   275  		binaryOnly:  true,
   276  		shouldBuild: false,
   277  	},
   278  	{
   279  		name: "ValidGoBuild",
   280  		content: "// +build yes\n\n" +
   281  			"//go:build no\n" +
   282  			"package main\n",
   283  		tags:        map[string]bool{"no": true},
   284  		shouldBuild: false,
   285  	},
   286  	{
   287  		name: "MissingBuild2",
   288  		content: "/* */\n" +
   289  			"// +build yes\n\n" +
   290  			"//go:build no\n" +
   291  			"package main\n",
   292  		tags:        map[string]bool{"no": true},
   293  		shouldBuild: false,
   294  	},
   295  	{
   296  		name: "Comment1",
   297  		content: "/*\n" +
   298  			"//go:build no\n" +
   299  			"*/\n\n" +
   300  			"package main\n",
   301  		tags:        map[string]bool{},
   302  		shouldBuild: true,
   303  	},
   304  	{
   305  		name: "Comment2",
   306  		content: "/*\n" +
   307  			"text\n" +
   308  			"*/\n\n" +
   309  			"//go:build no\n" +
   310  			"package main\n",
   311  		tags:        map[string]bool{"no": true},
   312  		shouldBuild: false,
   313  	},
   314  	{
   315  		name: "Comment3",
   316  		content: "/*/*/ /* hi *//* \n" +
   317  			"text\n" +
   318  			"*/\n\n" +
   319  			"//go:build no\n" +
   320  			"package main\n",
   321  		tags:        map[string]bool{"no": true},
   322  		shouldBuild: false,
   323  	},
   324  	{
   325  		name: "Comment4",
   326  		content: "/**///go:build no\n" +
   327  			"package main\n",
   328  		tags:        map[string]bool{},
   329  		shouldBuild: true,
   330  	},
   331  	{
   332  		name: "Comment5",
   333  		content: "/**/\n" +
   334  			"//go:build no\n" +
   335  			"package main\n",
   336  		tags:        map[string]bool{"no": true},
   337  		shouldBuild: false,
   338  	},
   339  }
   340  
   341  func TestShouldBuild(t *testing.T) {
   342  	for _, tt := range shouldBuildTests {
   343  		t.Run(tt.name, func(t *testing.T) {
   344  			ctx := &Context{BuildTags: []string{"yes"}}
   345  			tags := map[string]bool{}
   346  			shouldBuild, binaryOnly, err := ctx.shouldBuild([]byte(tt.content), tags)
   347  			if shouldBuild != tt.shouldBuild || binaryOnly != tt.binaryOnly || !reflect.DeepEqual(tags, tt.tags) || err != tt.err {
   348  				t.Errorf("mismatch:\n"+
   349  					"have shouldBuild=%v, binaryOnly=%v, tags=%v, err=%v\n"+
   350  					"want shouldBuild=%v, binaryOnly=%v, tags=%v, err=%v",
   351  					shouldBuild, binaryOnly, tags, err,
   352  					tt.shouldBuild, tt.binaryOnly, tt.tags, tt.err)
   353  			}
   354  		})
   355  	}
   356  }
   357  
   358  func TestGoodOSArchFile(t *testing.T) {
   359  	ctx := &Context{BuildTags: []string{"linux"}, GOOS: "darwin"}
   360  	m := map[string]bool{}
   361  	want := map[string]bool{"linux": true}
   362  	if !ctx.goodOSArchFile("hello_linux.go", m) {
   363  		t.Errorf("goodOSArchFile(hello_linux.go) = false, want true")
   364  	}
   365  	if !reflect.DeepEqual(m, want) {
   366  		t.Errorf("goodOSArchFile(hello_linux.go) tags = %v, want %v", m, want)
   367  	}
   368  }
   369  
   370  type readNopCloser struct {
   371  	io.Reader
   372  }
   373  
   374  func (r readNopCloser) Close() error {
   375  	return nil
   376  }
   377  
   378  var (
   379  	ctxtP9      = Context{GOARCH: "arm", GOOS: "plan9"}
   380  	ctxtAndroid = Context{GOARCH: "arm", GOOS: "android"}
   381  )
   382  
   383  var matchFileTests = []struct {
   384  	ctxt  Context
   385  	name  string
   386  	data  string
   387  	match bool
   388  }{
   389  	{ctxtP9, "foo_arm.go", "", true},
   390  	{ctxtP9, "foo1_arm.go", "// +build linux\n\npackage main\n", false},
   391  	{ctxtP9, "foo_darwin.go", "", false},
   392  	{ctxtP9, "foo.go", "", true},
   393  	{ctxtP9, "foo1.go", "// +build linux\n\npackage main\n", false},
   394  	{ctxtP9, "foo.badsuffix", "", false},
   395  	{ctxtAndroid, "foo_linux.go", "", true},
   396  	{ctxtAndroid, "foo_android.go", "", true},
   397  	{ctxtAndroid, "foo_plan9.go", "", false},
   398  	{ctxtAndroid, "android.go", "", true},
   399  	{ctxtAndroid, "plan9.go", "", true},
   400  	{ctxtAndroid, "plan9_test.go", "", true},
   401  	{ctxtAndroid, "arm.s", "", true},
   402  	{ctxtAndroid, "amd64.s", "", true},
   403  }
   404  
   405  func TestMatchFile(t *testing.T) {
   406  	for _, tt := range matchFileTests {
   407  		ctxt := tt.ctxt
   408  		ctxt.OpenFile = func(path string) (r io.ReadCloser, err error) {
   409  			if path != "x+"+tt.name {
   410  				t.Fatalf("OpenFile asked for %q, expected %q", path, "x+"+tt.name)
   411  			}
   412  			return &readNopCloser{strings.NewReader(tt.data)}, nil
   413  		}
   414  		ctxt.JoinPath = func(elem ...string) string {
   415  			return strings.Join(elem, "+")
   416  		}
   417  		match, err := ctxt.MatchFile("x", tt.name)
   418  		if match != tt.match || err != nil {
   419  			t.Fatalf("MatchFile(%q) = %v, %v, want %v, nil", tt.name, match, err, tt.match)
   420  		}
   421  	}
   422  }
   423  
   424  func TestImportCmd(t *testing.T) {
   425  	if runtime.GOOS == "ios" {
   426  		t.Skipf("skipping on %s/%s, no valid GOROOT", runtime.GOOS, runtime.GOARCH)
   427  	}
   428  
   429  	p, err := Import("cmd/internal/objfile", "", 0)
   430  	if err != nil {
   431  		t.Fatal(err)
   432  	}
   433  	if !strings.HasSuffix(filepath.ToSlash(p.Dir), "src/cmd/internal/objfile") {
   434  		t.Fatalf("Import cmd/internal/objfile returned Dir=%q, want %q", filepath.ToSlash(p.Dir), ".../src/cmd/internal/objfile")
   435  	}
   436  }
   437  
   438  var (
   439  	expandSrcDirPath = filepath.Join(string(filepath.Separator)+"projects", "src", "add")
   440  )
   441  
   442  var expandSrcDirTests = []struct {
   443  	input, expected string
   444  }{
   445  	{"-L ${SRCDIR}/libs -ladd", "-L /projects/src/add/libs -ladd"},
   446  	{"${SRCDIR}/add_linux_386.a -pthread -lstdc++", "/projects/src/add/add_linux_386.a -pthread -lstdc++"},
   447  	{"Nothing to expand here!", "Nothing to expand here!"},
   448  	{"$", "$"},
   449  	{"$$", "$$"},
   450  	{"${", "${"},
   451  	{"$}", "$}"},
   452  	{"$FOO ${BAR}", "$FOO ${BAR}"},
   453  	{"Find me the $SRCDIRECTORY.", "Find me the $SRCDIRECTORY."},
   454  	{"$SRCDIR is missing braces", "$SRCDIR is missing braces"},
   455  }
   456  
   457  func TestExpandSrcDir(t *testing.T) {
   458  	for _, test := range expandSrcDirTests {
   459  		output, _ := expandSrcDir(test.input, expandSrcDirPath)
   460  		if output != test.expected {
   461  			t.Errorf("%q expands to %q with SRCDIR=%q when %q is expected", test.input, output, expandSrcDirPath, test.expected)
   462  		} else {
   463  			t.Logf("%q expands to %q with SRCDIR=%q", test.input, output, expandSrcDirPath)
   464  		}
   465  	}
   466  }
   467  
   468  func TestShellSafety(t *testing.T) {
   469  	tests := []struct {
   470  		input, srcdir, expected string
   471  		result                  bool
   472  	}{
   473  		{"-I${SRCDIR}/../include", "/projects/src/issue 11868", "-I/projects/src/issue 11868/../include", true},
   474  		{"-I${SRCDIR}", "~wtf$@%^", "-I~wtf$@%^", true},
   475  		{"-X${SRCDIR}/1,${SRCDIR}/2", "/projects/src/issue 11868", "-X/projects/src/issue 11868/1,/projects/src/issue 11868/2", true},
   476  		{"-I/tmp -I/tmp", "/tmp2", "-I/tmp -I/tmp", true},
   477  		{"-I/tmp", "/tmp/[0]", "-I/tmp", true},
   478  		{"-I${SRCDIR}/dir", "/tmp/[0]", "-I/tmp/[0]/dir", false},
   479  		{"-I${SRCDIR}/dir", "/tmp/go go", "-I/tmp/go go/dir", true},
   480  		{"-I${SRCDIR}/dir dir", "/tmp/go", "-I/tmp/go/dir dir", true},
   481  	}
   482  	for _, test := range tests {
   483  		output, ok := expandSrcDir(test.input, test.srcdir)
   484  		if ok != test.result {
   485  			t.Errorf("Expected %t while %q expands to %q with SRCDIR=%q; got %t", test.result, test.input, output, test.srcdir, ok)
   486  		}
   487  		if output != test.expected {
   488  			t.Errorf("Expected %q while %q expands with SRCDIR=%q; got %q", test.expected, test.input, test.srcdir, output)
   489  		}
   490  	}
   491  }
   492  
   493  // Want to get a "cannot find package" error when directory for package does not exist.
   494  // There should be valid partial information in the returned non-nil *Package.
   495  func TestImportDirNotExist(t *testing.T) {
   496  	testenv.MustHaveGoBuild(t) // really must just have source
   497  	ctxt := Default
   498  
   499  	emptyDir := t.TempDir()
   500  
   501  	ctxt.GOPATH = emptyDir
   502  	ctxt.Dir = emptyDir
   503  
   504  	tests := []struct {
   505  		label        string
   506  		path, srcDir string
   507  		mode         ImportMode
   508  	}{
   509  		{"Import(full, 0)", "go/build/doesnotexist", "", 0},
   510  		{"Import(local, 0)", "./doesnotexist", filepath.Join(ctxt.GOROOT, "src/go/build"), 0},
   511  		{"Import(full, FindOnly)", "go/build/doesnotexist", "", FindOnly},
   512  		{"Import(local, FindOnly)", "./doesnotexist", filepath.Join(ctxt.GOROOT, "src/go/build"), FindOnly},
   513  	}
   514  
   515  	defer os.Setenv("GO111MODULE", os.Getenv("GO111MODULE"))
   516  
   517  	for _, GO111MODULE := range []string{"off", "on"} {
   518  		t.Run("GO111MODULE="+GO111MODULE, func(t *testing.T) {
   519  			os.Setenv("GO111MODULE", GO111MODULE)
   520  
   521  			for _, test := range tests {
   522  				p, err := ctxt.Import(test.path, test.srcDir, test.mode)
   523  
   524  				errOk := (err != nil && strings.HasPrefix(err.Error(), "cannot find package"))
   525  				wantErr := `"cannot find package" error`
   526  				if test.srcDir == "" {
   527  					if err != nil && strings.Contains(err.Error(), "is not in GOROOT") {
   528  						errOk = true
   529  					}
   530  					wantErr = `"cannot find package" or "is not in GOROOT" error`
   531  				}
   532  				if !errOk {
   533  					t.Errorf("%s got error: %q, want %s", test.label, err, wantErr)
   534  				}
   535  				// If an error occurs, build.Import is documented to return
   536  				// a non-nil *Package containing partial information.
   537  				if p == nil {
   538  					t.Fatalf(`%s got nil p, want non-nil *Package`, test.label)
   539  				}
   540  				// Verify partial information in p.
   541  				if p.ImportPath != "go/build/doesnotexist" {
   542  					t.Errorf(`%s got p.ImportPath: %q, want "go/build/doesnotexist"`, test.label, p.ImportPath)
   543  				}
   544  			}
   545  		})
   546  	}
   547  }
   548  
   549  func TestImportVendor(t *testing.T) {
   550  	testenv.MustHaveGoBuild(t) // really must just have source
   551  
   552  	t.Setenv("GO111MODULE", "off")
   553  
   554  	ctxt := Default
   555  	wd, err := os.Getwd()
   556  	if err != nil {
   557  		t.Fatal(err)
   558  	}
   559  	ctxt.GOPATH = filepath.Join(wd, "testdata/withvendor")
   560  	p, err := ctxt.Import("c/d", filepath.Join(ctxt.GOPATH, "src/a/b"), 0)
   561  	if err != nil {
   562  		t.Fatalf("cannot find vendored c/d from testdata src/a/b directory: %v", err)
   563  	}
   564  	want := "a/vendor/c/d"
   565  	if p.ImportPath != want {
   566  		t.Fatalf("Import succeeded but found %q, want %q", p.ImportPath, want)
   567  	}
   568  }
   569  
   570  func BenchmarkImportVendor(b *testing.B) {
   571  	testenv.MustHaveGoBuild(b) // really must just have source
   572  
   573  	b.Setenv("GO111MODULE", "off")
   574  
   575  	ctxt := Default
   576  	wd, err := os.Getwd()
   577  	if err != nil {
   578  		b.Fatal(err)
   579  	}
   580  	ctxt.GOPATH = filepath.Join(wd, "testdata/withvendor")
   581  	dir := filepath.Join(ctxt.GOPATH, "src/a/b")
   582  	b.ResetTimer()
   583  	for i := 0; i < b.N; i++ {
   584  		_, err := ctxt.Import("c/d", dir, 0)
   585  		if err != nil {
   586  			b.Fatalf("cannot find vendored c/d from testdata src/a/b directory: %v", err)
   587  		}
   588  	}
   589  }
   590  
   591  func TestImportVendorFailure(t *testing.T) {
   592  	testenv.MustHaveGoBuild(t) // really must just have source
   593  
   594  	t.Setenv("GO111MODULE", "off")
   595  
   596  	ctxt := Default
   597  	wd, err := os.Getwd()
   598  	if err != nil {
   599  		t.Fatal(err)
   600  	}
   601  	ctxt.GOPATH = filepath.Join(wd, "testdata/withvendor")
   602  	p, err := ctxt.Import("x.com/y/z", filepath.Join(ctxt.GOPATH, "src/a/b"), 0)
   603  	if err == nil {
   604  		t.Fatalf("found made-up package x.com/y/z in %s", p.Dir)
   605  	}
   606  
   607  	e := err.Error()
   608  	if !strings.Contains(e, " (vendor tree)") {
   609  		t.Fatalf("error on failed import does not mention GOROOT/src/vendor directory:\n%s", e)
   610  	}
   611  }
   612  
   613  func TestImportVendorParentFailure(t *testing.T) {
   614  	testenv.MustHaveGoBuild(t) // really must just have source
   615  
   616  	t.Setenv("GO111MODULE", "off")
   617  
   618  	ctxt := Default
   619  	wd, err := os.Getwd()
   620  	if err != nil {
   621  		t.Fatal(err)
   622  	}
   623  	ctxt.GOPATH = filepath.Join(wd, "testdata/withvendor")
   624  	// This import should fail because the vendor/c directory has no source code.
   625  	p, err := ctxt.Import("c", filepath.Join(ctxt.GOPATH, "src/a/b"), 0)
   626  	if err == nil {
   627  		t.Fatalf("found empty parent in %s", p.Dir)
   628  	}
   629  	if p != nil && p.Dir != "" {
   630  		t.Fatalf("decided to use %s", p.Dir)
   631  	}
   632  	e := err.Error()
   633  	if !strings.Contains(e, " (vendor tree)") {
   634  		t.Fatalf("error on failed import does not mention GOROOT/src/vendor directory:\n%s", e)
   635  	}
   636  }
   637  
   638  // Check that a package is loaded in module mode if GO111MODULE=on, even when
   639  // no go.mod file is present. It should fail to resolve packages outside std.
   640  // Verifies golang.org/issue/34669.
   641  func TestImportPackageOutsideModule(t *testing.T) {
   642  	testenv.MustHaveGoBuild(t)
   643  
   644  	// Disable module fetching for this test so that 'go list' fails quickly
   645  	// without trying to find the latest version of a module.
   646  	t.Setenv("GOPROXY", "off")
   647  
   648  	// Create a GOPATH in a temporary directory. We don't use testdata
   649  	// because it's in GOROOT, which interferes with the module heuristic.
   650  	gopath := t.TempDir()
   651  	if err := os.MkdirAll(filepath.Join(gopath, "src/example.com/p"), 0777); err != nil {
   652  		t.Fatal(err)
   653  	}
   654  	if err := os.WriteFile(filepath.Join(gopath, "src/example.com/p/p.go"), []byte("package p"), 0666); err != nil {
   655  		t.Fatal(err)
   656  	}
   657  
   658  	t.Setenv("GO111MODULE", "on")
   659  	t.Setenv("GOPATH", gopath)
   660  	ctxt := Default
   661  	ctxt.GOPATH = gopath
   662  	ctxt.Dir = filepath.Join(gopath, "src/example.com/p")
   663  
   664  	want := "go.mod file not found in current directory or any parent directory"
   665  	if _, err := ctxt.Import("example.com/p", gopath, FindOnly); err == nil {
   666  		t.Fatal("importing package when no go.mod is present succeeded unexpectedly")
   667  	} else if errStr := err.Error(); !strings.Contains(errStr, want) {
   668  		t.Fatalf("error when importing package when no go.mod is present: got %q; want %q", errStr, want)
   669  	} else {
   670  		t.Logf(`ctxt.Import("example.com/p", _, FindOnly): %v`, err)
   671  	}
   672  }
   673  
   674  // TestIssue23594 prevents go/build from regressing and populating Package.Doc
   675  // from comments in test files.
   676  func TestIssue23594(t *testing.T) {
   677  	// Package testdata/doc contains regular and external test files
   678  	// with comments attached to their package declarations. The names of the files
   679  	// ensure that we see the comments from the test files first.
   680  	p, err := ImportDir("testdata/doc", 0)
   681  	if err != nil {
   682  		t.Fatalf("could not import testdata: %v", err)
   683  	}
   684  
   685  	if p.Doc != "Correct" {
   686  		t.Fatalf("incorrectly set .Doc to %q", p.Doc)
   687  	}
   688  }
   689  
   690  // TestIssue56509 tests that go/build does not add non-go files to InvalidGoFiles
   691  // when they have unparsable comments.
   692  func TestIssue56509(t *testing.T) {
   693  	// The directory testdata/bads contains a .s file that has an unparsable
   694  	// comment. (go/build parses initial comments in non-go files looking for
   695  	// //go:build or //+go build comments).
   696  	p, err := ImportDir("testdata/bads", 0)
   697  	if err == nil {
   698  		t.Fatalf("could not import testdata/bads: %v", err)
   699  	}
   700  
   701  	if len(p.InvalidGoFiles) != 0 {
   702  		t.Fatalf("incorrectly added non-go file to InvalidGoFiles")
   703  	}
   704  }
   705  
   706  // TestMissingImportErrorRepetition checks that when an unknown package is
   707  // imported, the package path is only shown once in the error.
   708  // Verifies golang.org/issue/34752.
   709  func TestMissingImportErrorRepetition(t *testing.T) {
   710  	testenv.MustHaveGoBuild(t) // need 'go list' internally
   711  	tmp := t.TempDir()
   712  	if err := os.WriteFile(filepath.Join(tmp, "go.mod"), []byte("module m"), 0666); err != nil {
   713  		t.Fatal(err)
   714  	}
   715  	t.Setenv("GO111MODULE", "on")
   716  	t.Setenv("GOPROXY", "off")
   717  	t.Setenv("GONOPROXY", "none")
   718  
   719  	ctxt := Default
   720  	ctxt.Dir = tmp
   721  
   722  	pkgPath := "example.com/hello"
   723  	_, err := ctxt.Import(pkgPath, tmp, FindOnly)
   724  	if err == nil {
   725  		t.Fatal("unexpected success")
   726  	}
   727  
   728  	// Don't count the package path with a URL like https://...?go-get=1.
   729  	// See golang.org/issue/35986.
   730  	errStr := strings.ReplaceAll(err.Error(), "://"+pkgPath+"?go-get=1", "://...?go-get=1")
   731  
   732  	// Also don't count instances in suggested "go get" or similar commands
   733  	// (see https://golang.org/issue/41576). The suggested command typically
   734  	// follows a semicolon.
   735  	errStr, _, _ = strings.Cut(errStr, ";")
   736  
   737  	if n := strings.Count(errStr, pkgPath); n != 1 {
   738  		t.Fatalf("package path %q appears in error %d times; should appear once\nerror: %v", pkgPath, n, err)
   739  	}
   740  }
   741  
   742  // TestCgoImportsIgnored checks that imports in cgo files are not included
   743  // in the imports list when cgo is disabled.
   744  // Verifies golang.org/issue/35946.
   745  func TestCgoImportsIgnored(t *testing.T) {
   746  	ctxt := Default
   747  	ctxt.CgoEnabled = false
   748  	p, err := ctxt.ImportDir("testdata/cgo_disabled", 0)
   749  	if err != nil {
   750  		t.Fatal(err)
   751  	}
   752  	for _, path := range p.Imports {
   753  		if path == "should/be/ignored" {
   754  			t.Errorf("found import %q in ignored cgo file", path)
   755  		}
   756  	}
   757  }
   758  
   759  // Issue #52053. Check that if there is a file x_GOOS_GOARCH.go that both
   760  // GOOS and GOARCH show up in the Package.AllTags field. We test both the
   761  // case where the file matches and where the file does not match.
   762  // The latter case used to fail, incorrectly omitting GOOS.
   763  func TestAllTags(t *testing.T) {
   764  	ctxt := Default
   765  	ctxt.GOARCH = "arm"
   766  	ctxt.GOOS = "netbsd"
   767  	p, err := ctxt.ImportDir("testdata/alltags", 0)
   768  	if err != nil {
   769  		t.Fatal(err)
   770  	}
   771  	want := []string{"arm", "netbsd"}
   772  	if !reflect.DeepEqual(p.AllTags, want) {
   773  		t.Errorf("AllTags = %v, want %v", p.AllTags, want)
   774  	}
   775  	wantFiles := []string{"alltags.go", "x_netbsd_arm.go"}
   776  	if !reflect.DeepEqual(p.GoFiles, wantFiles) {
   777  		t.Errorf("GoFiles = %v, want %v", p.GoFiles, wantFiles)
   778  	}
   779  
   780  	ctxt.GOARCH = "amd64"
   781  	ctxt.GOOS = "linux"
   782  	p, err = ctxt.ImportDir("testdata/alltags", 0)
   783  	if err != nil {
   784  		t.Fatal(err)
   785  	}
   786  	if !reflect.DeepEqual(p.AllTags, want) {
   787  		t.Errorf("AllTags = %v, want %v", p.AllTags, want)
   788  	}
   789  	wantFiles = []string{"alltags.go"}
   790  	if !reflect.DeepEqual(p.GoFiles, wantFiles) {
   791  		t.Errorf("GoFiles = %v, want %v", p.GoFiles, wantFiles)
   792  	}
   793  }
   794  
   795  func TestAllTagsNonSourceFile(t *testing.T) {
   796  	p, err := Default.ImportDir("testdata/non_source_tags", 0)
   797  	if err != nil {
   798  		t.Fatal(err)
   799  	}
   800  	if len(p.AllTags) > 0 {
   801  		t.Errorf("AllTags = %v, want empty", p.AllTags)
   802  	}
   803  }
   804  

View as plain text