| /* Copyright 2017 The Bazel Authors. All rights reserved. |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| package golang |
| |
| import ( |
| "go/build/constraint" |
| "os" |
| "path/filepath" |
| "strings" |
| "testing" |
| |
| "github.com/google/go-cmp/cmp" |
| ) |
| |
| var ( |
| fileInfoCmpOption = cmp.AllowUnexported( |
| fileInfo{}, |
| fileEmbed{}, |
| buildTags{}, |
| cgoTagsAndOpts{}, |
| ) |
| ) |
| |
| func TestGoFileInfo(t *testing.T) { |
| for _, tc := range []struct { |
| desc, name, source string |
| want fileInfo |
| }{ |
| { |
| "empty file", |
| "foo.go", |
| "package foo\n", |
| fileInfo{ |
| packageName: "foo", |
| }, |
| }, |
| { |
| "xtest file", |
| "foo_test.go", |
| "package foo_test\n", |
| fileInfo{ |
| packageName: "foo", |
| isTest: true, |
| }, |
| }, |
| { |
| "xtest suffix on non-test", |
| "foo_xtest.go", |
| "package foo_test\n", |
| fileInfo{ |
| packageName: "foo_test", |
| isTest: false, |
| }, |
| }, |
| { |
| "single import", |
| "foo.go", |
| `package foo |
| |
| import "github.com/foo/bar" |
| `, |
| fileInfo{ |
| packageName: "foo", |
| imports: []string{"github.com/foo/bar"}, |
| }, |
| }, |
| { |
| "multiple imports", |
| "foo.go", |
| `package foo |
| |
| import ( |
| "github.com/foo/bar" |
| x "github.com/local/project/y" |
| ) |
| `, |
| fileInfo{ |
| packageName: "foo", |
| imports: []string{"github.com/foo/bar", "github.com/local/project/y"}, |
| }, |
| }, |
| { |
| "standard imports included", |
| "foo.go", |
| `package foo |
| |
| import "fmt" |
| `, |
| fileInfo{ |
| packageName: "foo", |
| imports: []string{"fmt"}, |
| }, |
| }, |
| { |
| "cgo", |
| "foo.go", |
| `package foo |
| |
| import "C" |
| `, |
| fileInfo{ |
| packageName: "foo", |
| isCgo: true, |
| }, |
| }, |
| { |
| "build tags", |
| "foo.go", |
| `// +build linux darwin |
| |
| // +build !ignore |
| |
| package foo |
| `, |
| fileInfo{ |
| packageName: "foo", |
| tags: &buildTags{ |
| expr: mustParseBuildTag(t, "(linux || darwin) && !ignore"), |
| rawTags: []string{"linux", "darwin", "ignore"}, |
| }, |
| }, |
| }, |
| { |
| "build tags without blank line", |
| "route.go", |
| `// Copyright 2017 |
| |
| // +build darwin dragonfly freebsd netbsd openbsd |
| |
| // Package route provides basic functions for the manipulation of |
| // packet routing facilities on BSD variants. |
| package route |
| `, |
| fileInfo{ |
| packageName: "route", |
| tags: &buildTags{ |
| expr: mustParseBuildTag(t, "darwin || dragonfly || freebsd || netbsd || openbsd"), |
| rawTags: []string{"darwin", "dragonfly", "freebsd", "netbsd", "openbsd"}, |
| }, |
| }, |
| }, |
| { |
| "embed", |
| "embed.go", |
| `package foo |
| |
| import _ "embed" |
| |
| //go:embed embed.go |
| var src string |
| `, |
| fileInfo{ |
| packageName: "foo", |
| imports: []string{"embed"}, |
| embeds: []fileEmbed{{path: "embed.go"}}, |
| }, |
| }, |
| } { |
| t.Run(tc.desc, func(t *testing.T) { |
| dir, err := os.MkdirTemp(os.Getenv("TEST_TEMPDIR"), "TestGoFileInfo") |
| if err != nil { |
| t.Fatal(err) |
| } |
| defer os.RemoveAll(dir) |
| path := filepath.Join(dir, tc.name) |
| if err := os.WriteFile(path, []byte(tc.source), 0o600); err != nil { |
| t.Fatal(err) |
| } |
| |
| got := goFileInfo(path, "") |
| // Clear fields we don't care about for testing. |
| got = fileInfo{ |
| packageName: got.packageName, |
| isTest: got.isTest, |
| imports: got.imports, |
| embeds: got.embeds, |
| isCgo: got.isCgo, |
| tags: got.tags, |
| } |
| for i := range got.embeds { |
| got.embeds[i] = fileEmbed{path: got.embeds[i].path} |
| } |
| |
| if diff := cmp.Diff(tc.want, got, fileInfoCmpOption); diff != "" { |
| t.Errorf("(-want, +got): %s", diff) |
| } |
| |
| }) |
| } |
| } |
| |
| func TestGoFileInfoFailure(t *testing.T) { |
| dir, err := os.MkdirTemp(os.Getenv("TEST_TEMPDIR"), "TestGoFileInfoFailure") |
| if err != nil { |
| t.Fatal(err) |
| } |
| defer os.RemoveAll(dir) |
| name := "foo_linux_amd64.go" |
| path := filepath.Join(dir, name) |
| if err := os.WriteFile(path, []byte("pakcage foo"), 0o600); err != nil { |
| t.Fatal(err) |
| } |
| |
| got := goFileInfo(path, "") |
| want := fileInfo{ |
| path: path, |
| name: name, |
| ext: goExt, |
| goos: "linux", |
| goarch: "amd64", |
| } |
| if diff := cmp.Diff(want, got, fileInfoCmpOption); diff != "" { |
| t.Errorf("(-want, +got): %s", diff) |
| } |
| |
| } |
| |
| func TestCgo(t *testing.T) { |
| for _, tc := range []struct { |
| desc, source string |
| want fileInfo |
| }{ |
| { |
| "not cgo", |
| "package foo\n", |
| fileInfo{isCgo: false}, |
| }, |
| { |
| "empty cgo", |
| `package foo |
| |
| import "C" |
| `, |
| fileInfo{isCgo: true}, |
| }, |
| { |
| "simple flags", |
| `package foo |
| |
| /* |
| #cgo CFLAGS: -O0 |
| #cgo CPPFLAGS: -O1 |
| #cgo CXXFLAGS: -O2 |
| #cgo LDFLAGS: -O3 -O4 |
| */ |
| import "C" |
| `, |
| fileInfo{ |
| isCgo: true, |
| cppopts: []*cgoTagsAndOpts{ |
| {opts: "-O1"}, |
| }, |
| copts: []*cgoTagsAndOpts{ |
| {opts: "-O0"}, |
| }, |
| cxxopts: []*cgoTagsAndOpts{ |
| {opts: "-O2"}, |
| }, |
| clinkopts: []*cgoTagsAndOpts{ |
| {opts: strings.Join([]string{"-O3", "-O4"}, optSeparator)}, |
| }, |
| }, |
| }, |
| { |
| "cflags with conditions", |
| `package foo |
| |
| /* |
| #cgo foo bar,!baz CFLAGS: -O0 |
| */ |
| import "C" |
| `, |
| fileInfo{ |
| isCgo: true, |
| copts: []*cgoTagsAndOpts{ |
| { |
| buildTags: &buildTags{ |
| expr: mustParseBuildTag(t, "foo || (bar && !baz)"), |
| rawTags: []string{"foo", "bar", "baz"}, |
| }, |
| opts: "-O0", |
| }, |
| }, |
| }, |
| }, |
| { |
| "slashslash comments", |
| `package foo |
| |
| // #cgo CFLAGS: -O0 |
| // #cgo CFLAGS: -O1 |
| import "C" |
| `, |
| fileInfo{ |
| isCgo: true, |
| copts: []*cgoTagsAndOpts{ |
| {opts: "-O0"}, |
| {opts: "-O1"}, |
| }, |
| }, |
| }, |
| { |
| "comment above single import group", |
| `package foo |
| |
| /* |
| #cgo CFLAGS: -O0 |
| */ |
| import ("C") |
| `, |
| fileInfo{ |
| isCgo: true, |
| copts: []*cgoTagsAndOpts{ |
| {opts: "-O0"}, |
| }, |
| }, |
| }, |
| } { |
| t.Run(tc.desc, func(t *testing.T) { |
| dir, err := os.MkdirTemp(os.Getenv("TEST_TEMPDIR"), "TestCgo") |
| if err != nil { |
| t.Fatal(err) |
| } |
| defer os.RemoveAll(dir) |
| name := "TestCgo.go" |
| path := filepath.Join(dir, name) |
| if err := os.WriteFile(path, []byte(tc.source), 0o600); err != nil { |
| t.Fatal(err) |
| } |
| |
| got := goFileInfo(path, "") |
| |
| // Clear fields we don't care about for testing. |
| got = fileInfo{ |
| isCgo: got.isCgo, |
| copts: got.copts, |
| cppopts: got.cppopts, |
| cxxopts: got.cxxopts, |
| clinkopts: got.clinkopts, |
| } |
| |
| if diff := cmp.Diff(tc.want, got, fileInfoCmpOption); diff != "" { |
| t.Errorf("(-want, +got): %s", diff) |
| } |
| |
| }) |
| } |
| } |
| |
| // Copied from go/build build_test.go |
| var ( |
| expandSrcDirPath = filepath.Join(string(filepath.Separator)+"projects", "src", "add") |
| ) |
| |
| // Copied from go/build build_test.go |
| var expandSrcDirTests = []struct { |
| input, expected string |
| }{ |
| {"-L ${SRCDIR}/libs -ladd", "-L /projects/src/add/libs -ladd"}, |
| {"${SRCDIR}/add_linux_386.a -pthread -lstdc++", "/projects/src/add/add_linux_386.a -pthread -lstdc++"}, |
| {"Nothing to expand here!", "Nothing to expand here!"}, |
| {"$", "$"}, |
| {"$$", "$$"}, |
| {"${", "${"}, |
| {"$}", "$}"}, |
| {"$FOO ${BAR}", "$FOO ${BAR}"}, |
| {"Find me the $SRCDIRECTORY.", "Find me the $SRCDIRECTORY."}, |
| {"$SRCDIR is missing braces", "$SRCDIR is missing braces"}, |
| } |
| |
| // Copied from go/build build_test.go |
| func TestExpandSrcDir(t *testing.T) { |
| for _, test := range expandSrcDirTests { |
| output, _ := expandSrcDir(test.input, expandSrcDirPath) |
| if output != test.expected { |
| t.Errorf("%q expands to %q with SRCDIR=%q when %q is expected", test.input, output, expandSrcDirPath, test.expected) |
| } else { |
| t.Logf("%q expands to %q with SRCDIR=%q", test.input, output, expandSrcDirPath) |
| } |
| } |
| } |
| |
| var ( |
| goPackageCmpOption = cmp.AllowUnexported( |
| goPackage{}, |
| goTarget{}, |
| protoTarget{}, |
| platformStringsBuilder{}, |
| platformStringInfo{}, |
| ) |
| ) |
| |
| func TestExpandSrcDirRepoRelative(t *testing.T) { |
| repo, err := os.MkdirTemp(os.Getenv("TEST_TEMPDIR"), "repo") |
| if err != nil { |
| t.Fatal(err) |
| } |
| sub := filepath.Join(repo, "sub") |
| if err := os.Mkdir(sub, 0o755); err != nil { |
| t.Fatal(err) |
| } |
| goFile := filepath.Join(sub, "sub.go") |
| content := []byte(`package sub |
| |
| /* |
| #cgo CFLAGS: -I${SRCDIR}/.. |
| */ |
| import "C" |
| `) |
| if err := os.WriteFile(goFile, content, 0o644); err != nil { |
| t.Fatal(err) |
| } |
| c, _, _ := testConfig( |
| t, |
| "-repo_root="+repo, |
| "-go_prefix=example.com/repo") |
| fi := goFileInfo(filepath.Join(sub, "sub.go"), "sub") |
| pkgs, _ := buildPackages(c, sub, "sub", false, nil, []fileInfo{fi}) |
| got, ok := pkgs["sub"] |
| if !ok { |
| t.Fatal("did not build package 'sub'") |
| } |
| want := &goPackage{ |
| name: "sub", |
| dir: sub, |
| rel: "sub", |
| library: goTarget{cgo: true}, |
| } |
| want.library.sources.addGenericString("sub.go") |
| want.library.copts.addGenericString("-Isub/..") |
| if diff := cmp.Diff(want, got, goPackageCmpOption); diff != "" { |
| t.Errorf("(-want, +got): %s", diff) |
| } |
| |
| } |
| |
| // Copied from go/build build_test.go |
| func TestShellSafety(t *testing.T) { |
| tests := []struct { |
| input, srcdir, expected string |
| result bool |
| }{ |
| {"-I${SRCDIR}/../include", "/projects/src/issue 11868", "-I/projects/src/issue 11868/../include", true}, |
| {"-I${SRCDIR}", "wtf$@%", "-Iwtf$@%", true}, |
| {"-X${SRCDIR}/1,${SRCDIR}/2", "/projects/src/issue 11868", "-X/projects/src/issue 11868/1,/projects/src/issue 11868/2", true}, |
| {"-I/tmp -I/tmp", "/tmp2", "-I/tmp -I/tmp", false}, |
| {"-I/tmp", "/tmp/[0]", "-I/tmp", true}, |
| {"-I${SRCDIR}/dir", "/tmp/[0]", "-I/tmp/[0]/dir", false}, |
| } |
| for _, test := range tests { |
| output, ok := expandSrcDir(test.input, test.srcdir) |
| if ok != test.result { |
| t.Errorf("Expected %t while %q expands to %q with SRCDIR=%q; got %t", test.result, test.input, output, test.srcdir, ok) |
| } |
| if output != test.expected { |
| t.Errorf("Expected %q while %q expands with SRCDIR=%q; got %q", test.expected, test.input, test.srcdir, output) |
| } |
| } |
| } |
| |
| func mustParseBuildTag(t *testing.T, in string) constraint.Expr { |
| x, err := constraint.Parse("//go:build " + in) |
| if err != nil { |
| t.Fatalf("%s: %s", in, err) |
| } |
| |
| return x |
| } |