blob: ed51522e3e75bc955681843f9f461ebdb8657eb3 [file] [log] [blame]
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build cgo
package packages_test
import (
"fmt"
"os"
"runtime"
"strings"
"testing"
"golang.org/x/tools/go/packages"
"golang.org/x/tools/go/packages/packagestest"
"golang.org/x/tools/internal/testenv"
)
func TestLoadImportsC(t *testing.T) {
// This test checks that when a package depends on the
// test variant of "syscall", "unsafe", or "runtime/cgo", that dependency
// is not removed when those packages are added when it imports "C".
//
// For this test to work, the external test of syscall must have a dependency
// on net, and net must import "syscall" and "C".
if runtime.GOOS == "windows" {
t.Skipf("skipping on windows; packages on windows do not satisfy conditions for test.")
}
if runtime.GOOS == "plan9" {
// See https://golang.org/issue/27100.
t.Skip(`skipping on plan9; for some reason "net [syscall.test]" is not loaded`)
}
testenv.NeedsGoPackages(t)
cfg := &packages.Config{
Context: testCtx,
Mode: packages.LoadImports,
Tests: true,
}
initial, err := packages.Load(cfg, "syscall", "net")
if err != nil {
t.Fatalf("failed to load imports: %v", err)
}
_, all := importGraph(initial)
for _, test := range []struct {
pattern string
wantImport string // an import to check for
}{
{"net", "syscall:syscall"},
{"net [syscall.test]", "syscall:syscall [syscall.test]"},
{"syscall_test [syscall.test]", "net:net [syscall.test]"},
} {
// Test the import paths.
pkg := all[test.pattern]
if pkg == nil {
t.Errorf("package %q not loaded", test.pattern)
continue
}
if imports := strings.Join(imports(pkg), " "); !strings.Contains(imports, test.wantImport) {
t.Errorf("package %q: got \n%s, \nwant to have %s", test.pattern, imports, test.wantImport)
}
}
}
func TestCgoNoSyntax(t *testing.T) {
packagestest.TestAll(t, testCgoNoSyntax)
}
// Stolen from internal/testenv package in core.
// hasGoBuild reports whether the current system can build programs with ``go build''
// and then run them with os.StartProcess or exec.Command.
func hasGoBuild() bool {
if os.Getenv("GO_GCFLAGS") != "" {
// It's too much work to require every caller of the go command
// to pass along "-gcflags="+os.Getenv("GO_GCFLAGS").
// For now, if $GO_GCFLAGS is set, report that we simply can't
// run go build.
return false
}
switch runtime.GOOS {
case "android", "js":
return false
case "darwin":
if strings.HasPrefix(runtime.GOARCH, "arm") {
return false
}
}
return true
}
func testCgoNoSyntax(t *testing.T, exporter packagestest.Exporter) {
// The android builders have a complex setup which causes this test to fail. See discussion on
// golang.org/cl/214943 for more details.
if !hasGoBuild() {
t.Skip("this test can't run on platforms without go build. See discussion on golang.org/cl/214943 for more details.")
}
exported := packagestest.Export(t, exporter, []packagestest.Module{{
Name: "golang.org/fake",
Files: map[string]interface{}{
"c/c.go": `package c; import "C"`,
},
}})
// Explicitly enable cgo.
exported.Config.Env = append(exported.Config.Env, "CGO_ENABLED=1")
modes := []packages.LoadMode{
packages.NeedTypes,
packages.NeedName | packages.NeedTypes,
packages.NeedName | packages.NeedTypes | packages.NeedImports,
packages.NeedName | packages.NeedTypes | packages.NeedImports | packages.NeedDeps,
packages.NeedName | packages.NeedImports,
}
for _, mode := range modes {
t.Run(fmt.Sprint(mode), func(t *testing.T) {
exported.Config.Mode = mode
pkgs, err := packages.Load(exported.Config, "golang.org/fake/c")
if err != nil {
t.Fatal(err)
}
if len(pkgs) != 1 {
t.Fatalf("Expected 1 package, got %v", pkgs)
}
pkg := pkgs[0]
if len(pkg.Errors) != 0 {
t.Fatalf("Expected no errors in package, got %v", pkg.Errors)
}
})
}
}