Home | History | Annotate | Download | only in gcimporter
      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 gcimporter
      6 
      7 import (
      8 	"fmt"
      9 	"internal/testenv"
     10 	"io/ioutil"
     11 	"os"
     12 	"os/exec"
     13 	"path/filepath"
     14 	"runtime"
     15 	"strings"
     16 	"testing"
     17 	"time"
     18 
     19 	"go/types"
     20 )
     21 
     22 // skipSpecialPlatforms causes the test to be skipped for platforms where
     23 // builders (build.golang.org) don't have access to compiled packages for
     24 // import.
     25 func skipSpecialPlatforms(t *testing.T) {
     26 	switch platform := runtime.GOOS + "-" + runtime.GOARCH; platform {
     27 	case "nacl-amd64p32",
     28 		"nacl-386",
     29 		"nacl-arm",
     30 		"darwin-arm",
     31 		"darwin-arm64":
     32 		t.Skipf("no compiled packages available for import on %s", platform)
     33 	}
     34 }
     35 
     36 func compile(t *testing.T, dirname, filename string) string {
     37 	testenv.MustHaveGoBuild(t)
     38 	cmd := exec.Command("go", "tool", "compile", filename)
     39 	cmd.Dir = dirname
     40 	out, err := cmd.CombinedOutput()
     41 	if err != nil {
     42 		t.Logf("%s", out)
     43 		t.Fatalf("go tool compile %s failed: %s", filename, err)
     44 	}
     45 	// filename should end with ".go"
     46 	return filepath.Join(dirname, filename[:len(filename)-2]+"o")
     47 }
     48 
     49 // Use the same global imports map for all tests. The effect is
     50 // as if all tested packages were imported into a single package.
     51 var imports = make(map[string]*types.Package)
     52 
     53 func testPath(t *testing.T, path string) *types.Package {
     54 	t0 := time.Now()
     55 	pkg, err := Import(imports, path)
     56 	if err != nil {
     57 		t.Errorf("testPath(%s): %s", path, err)
     58 		return nil
     59 	}
     60 	t.Logf("testPath(%s): %v", path, time.Since(t0))
     61 	return pkg
     62 }
     63 
     64 const maxTime = 30 * time.Second
     65 
     66 func testDir(t *testing.T, dir string, endTime time.Time) (nimports int) {
     67 	dirname := filepath.Join(runtime.GOROOT(), "pkg", runtime.GOOS+"_"+runtime.GOARCH, dir)
     68 	list, err := ioutil.ReadDir(dirname)
     69 	if err != nil {
     70 		t.Fatalf("testDir(%s): %s", dirname, err)
     71 	}
     72 	for _, f := range list {
     73 		if time.Now().After(endTime) {
     74 			t.Log("testing time used up")
     75 			return
     76 		}
     77 		switch {
     78 		case !f.IsDir():
     79 			// try extensions
     80 			for _, ext := range pkgExts {
     81 				if strings.HasSuffix(f.Name(), ext) {
     82 					name := f.Name()[0 : len(f.Name())-len(ext)] // remove extension
     83 					if testPath(t, filepath.Join(dir, name)) != nil {
     84 						nimports++
     85 					}
     86 				}
     87 			}
     88 		case f.IsDir():
     89 			nimports += testDir(t, filepath.Join(dir, f.Name()), endTime)
     90 		}
     91 	}
     92 	return
     93 }
     94 
     95 func TestImport(t *testing.T) {
     96 	// This package only handles gc export data.
     97 	if runtime.Compiler != "gc" {
     98 		t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
     99 		return
    100 	}
    101 
    102 	if outFn := compile(t, "testdata", "exports.go"); outFn != "" {
    103 		defer os.Remove(outFn)
    104 	}
    105 
    106 	nimports := 0
    107 	if pkg := testPath(t, "./testdata/exports"); pkg != nil {
    108 		nimports++
    109 		// The package's Imports should include all the types
    110 		// referenced by the exportdata, which may be more than
    111 		// the import statements in the package's source, but
    112 		// fewer than the transitive closure of dependencies.
    113 		want := `[package ast ("go/ast") package token ("go/token") package runtime ("runtime")]`
    114 		got := fmt.Sprint(pkg.Imports())
    115 		if got != want {
    116 			t.Errorf(`Package("exports").Imports() = %s, want %s`, got, want)
    117 		}
    118 	}
    119 	nimports += testDir(t, "", time.Now().Add(maxTime)) // installed packages
    120 	t.Logf("tested %d imports", nimports)
    121 }
    122 
    123 var importedObjectTests = []struct {
    124 	name string
    125 	want string
    126 }{
    127 	{"math.Pi", "const Pi untyped float"},
    128 	{"io.Reader", "type Reader interface{Read(p []byte) (n int, err error)}"},
    129 	{"io.ReadWriter", "type ReadWriter interface{Read(p []byte) (n int, err error); Write(p []byte) (n int, err error)}"},
    130 	{"math.Sin", "func Sin(x float64) float64"},
    131 	// TODO(gri) add more tests
    132 }
    133 
    134 func TestImportedTypes(t *testing.T) {
    135 	skipSpecialPlatforms(t)
    136 
    137 	// This package only handles gc export data.
    138 	if runtime.Compiler != "gc" {
    139 		t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
    140 		return
    141 	}
    142 
    143 	for _, test := range importedObjectTests {
    144 		s := strings.Split(test.name, ".")
    145 		if len(s) != 2 {
    146 			t.Fatal("inconsistent test data")
    147 		}
    148 		importPath := s[0]
    149 		objName := s[1]
    150 
    151 		pkg, err := Import(imports, importPath)
    152 		if err != nil {
    153 			t.Error(err)
    154 			continue
    155 		}
    156 
    157 		obj := pkg.Scope().Lookup(objName)
    158 		if obj == nil {
    159 			t.Errorf("%s: object not found", test.name)
    160 			continue
    161 		}
    162 
    163 		got := types.ObjectString(obj, types.RelativeTo(pkg))
    164 		if got != test.want {
    165 			t.Errorf("%s: got %q; want %q", test.name, got, test.want)
    166 		}
    167 	}
    168 }
    169 
    170 func TestIssue5815(t *testing.T) {
    171 	skipSpecialPlatforms(t)
    172 
    173 	// This package only handles gc export data.
    174 	if runtime.Compiler != "gc" {
    175 		t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
    176 		return
    177 	}
    178 
    179 	pkg, err := Import(make(map[string]*types.Package), "strings")
    180 	if err != nil {
    181 		t.Fatal(err)
    182 	}
    183 
    184 	scope := pkg.Scope()
    185 	for _, name := range scope.Names() {
    186 		obj := scope.Lookup(name)
    187 		if obj.Pkg() == nil {
    188 			t.Errorf("no pkg for %s", obj)
    189 		}
    190 		if tname, _ := obj.(*types.TypeName); tname != nil {
    191 			named := tname.Type().(*types.Named)
    192 			for i := 0; i < named.NumMethods(); i++ {
    193 				m := named.Method(i)
    194 				if m.Pkg() == nil {
    195 					t.Errorf("no pkg for %s", m)
    196 				}
    197 			}
    198 		}
    199 	}
    200 }
    201 
    202 // Smoke test to ensure that imported methods get the correct package.
    203 func TestCorrectMethodPackage(t *testing.T) {
    204 	skipSpecialPlatforms(t)
    205 
    206 	// This package only handles gc export data.
    207 	if runtime.Compiler != "gc" {
    208 		t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
    209 		return
    210 	}
    211 
    212 	imports := make(map[string]*types.Package)
    213 	_, err := Import(imports, "net/http")
    214 	if err != nil {
    215 		t.Fatal(err)
    216 	}
    217 
    218 	mutex := imports["sync"].Scope().Lookup("Mutex").(*types.TypeName).Type()
    219 	mset := types.NewMethodSet(types.NewPointer(mutex)) // methods of *sync.Mutex
    220 	sel := mset.Lookup(nil, "Lock")
    221 	lock := sel.Obj().(*types.Func)
    222 	if got, want := lock.Pkg().Path(), "sync"; got != want {
    223 		t.Errorf("got package path %q; want %q", got, want)
    224 	}
    225 }
    226