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

View as plain text