blob: a4e4543752e7c389e4f6457176d710fcd959a874 [file] [log] [blame]
/* 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 (
"io/ioutil"
"os"
"path/filepath"
"reflect"
"strings"
"testing"
)
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: []tagLine{{{"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: []tagLine{{{"darwin"}, {"dragonfly"}, {"freebsd"}, {"netbsd"}, {"openbsd"}}},
},
},
} {
t.Run(tc.desc, func(t *testing.T) {
dir, err := ioutil.TempDir(os.Getenv("TEST_TEMPDIR"), "TestGoFileInfo")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir)
path := filepath.Join(dir, tc.name)
if err := ioutil.WriteFile(path, []byte(tc.source), 0600); 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,
isCgo: got.isCgo,
tags: got.tags,
}
if !reflect.DeepEqual(got, tc.want) {
t.Errorf("case %q: got %#v; want %#v", tc.desc, got, tc.want)
}
})
}
}
func TestGoFileInfoFailure(t *testing.T) {
dir, err := ioutil.TempDir(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 := ioutil.WriteFile(path, []byte("pakcage foo"), 0600); err != nil {
t.Fatal(err)
}
got := goFileInfo(path, "")
want := fileInfo{
path: path,
name: name,
ext: goExt,
goos: "linux",
goarch: "amd64",
}
if !reflect.DeepEqual(got, want) {
t.Errorf("got %#v ; want %#v", got, want)
}
}
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,
copts: []taggedOpts{
{opts: "-O0"},
{opts: "-O1"},
{opts: "-O2"},
},
clinkopts: []taggedOpts{
{opts: strings.Join([]string{"-O3", "-O4"}, optSeparator)},
},
},
},
{
"cflags with conditions",
`package foo
/*
#cgo foo bar,!baz CFLAGS: -O0
*/
import "C"
`,
fileInfo{
isCgo: true,
copts: []taggedOpts{
{
tags: tagLine{{"foo"}, {"bar", "!baz"}},
opts: "-O0",
},
},
},
},
{
"slashslash comments",
`package foo
// #cgo CFLAGS: -O0
// #cgo CFLAGS: -O1
import "C"
`,
fileInfo{
isCgo: true,
copts: []taggedOpts{
{opts: "-O0"},
{opts: "-O1"},
},
},
},
{
"comment above single import group",
`package foo
/*
#cgo CFLAGS: -O0
*/
import ("C")
`,
fileInfo{
isCgo: true,
copts: []taggedOpts{
{opts: "-O0"},
},
},
},
} {
t.Run(tc.desc, func(t *testing.T) {
dir, err := ioutil.TempDir(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 := ioutil.WriteFile(path, []byte(tc.source), 0600); 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, clinkopts: got.clinkopts}
if !reflect.DeepEqual(got, tc.want) {
t.Errorf("case %q: got %#v; want %#v", tc.desc, got, tc.want)
}
})
}
}
// 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)
}
}
}
func TestExpandSrcDirRepoRelative(t *testing.T) {
repo, err := ioutil.TempDir(os.Getenv("TEST_TEMPDIR"), "repo")
if err != nil {
t.Fatal(err)
}
sub := filepath.Join(repo, "sub")
if err := os.Mkdir(sub, 0755); err != nil {
t.Fatal(err)
}
goFile := filepath.Join(sub, "sub.go")
content := []byte(`package sub
/*
#cgo CFLAGS: -I${SRCDIR}/..
*/
import "C"
`)
if err := ioutil.WriteFile(goFile, content, 0644); err != nil {
t.Fatal(err)
}
c, _, _ := testConfig(
t,
"-repo_root="+repo,
"-go_prefix=example.com/repo")
pkgs, _ := buildPackages(c, sub, "sub", []string{"sub.go"}, false)
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 !reflect.DeepEqual(got, want) {
t.Errorf("got %#v ; want %#v", got, want)
}
}
// 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)
}
}
}