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