github.com/gocuntian/go@v0.0.0-20160610041250-fee02d270bf8/src/go/internal/gcimporter/gcimporter_test.go (about) 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 // TODO(gri) Remove this function once we switched to new export format by default. 50 func compileNewExport(t *testing.T, dirname, filename string) string { 51 testenv.MustHaveGoBuild(t) 52 cmd := exec.Command("go", "tool", "compile", "-newexport", filename) 53 cmd.Dir = dirname 54 out, err := cmd.CombinedOutput() 55 if err != nil { 56 t.Logf("%s", out) 57 t.Fatalf("go tool compile %s failed: %s", filename, err) 58 } 59 // filename should end with ".go" 60 return filepath.Join(dirname, filename[:len(filename)-2]+"o") 61 } 62 63 func testPath(t *testing.T, path, srcDir string) *types.Package { 64 t0 := time.Now() 65 pkg, err := Import(make(map[string]*types.Package), path, srcDir) 66 if err != nil { 67 t.Errorf("testPath(%s): %s", path, err) 68 return nil 69 } 70 t.Logf("testPath(%s): %v", path, time.Since(t0)) 71 return pkg 72 } 73 74 const maxTime = 30 * time.Second 75 76 func testDir(t *testing.T, dir string, endTime time.Time) (nimports int) { 77 dirname := filepath.Join(runtime.GOROOT(), "pkg", runtime.GOOS+"_"+runtime.GOARCH, dir) 78 list, err := ioutil.ReadDir(dirname) 79 if err != nil { 80 t.Fatalf("testDir(%s): %s", dirname, err) 81 } 82 for _, f := range list { 83 if time.Now().After(endTime) { 84 t.Log("testing time used up") 85 return 86 } 87 switch { 88 case !f.IsDir(): 89 // try extensions 90 for _, ext := range pkgExts { 91 if strings.HasSuffix(f.Name(), ext) { 92 name := f.Name()[0 : len(f.Name())-len(ext)] // remove extension 93 if testPath(t, filepath.Join(dir, name), dir) != nil { 94 nimports++ 95 } 96 } 97 } 98 case f.IsDir(): 99 nimports += testDir(t, filepath.Join(dir, f.Name()), endTime) 100 } 101 } 102 return 103 } 104 105 func TestImportTestdata(t *testing.T) { 106 // This package only handles gc export data. 107 if runtime.Compiler != "gc" { 108 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) 109 return 110 } 111 112 if outFn := compile(t, "testdata", "exports.go"); outFn != "" { 113 defer os.Remove(outFn) 114 } 115 116 if pkg := testPath(t, "./testdata/exports", "."); pkg != nil { 117 // The package's Imports list must include all packages 118 // explicitly imported by exports.go, plus all packages 119 // referenced indirectly via exported objects in exports.go. 120 // With the textual export format, the list may also include 121 // additional packages that are not strictly required for 122 // import processing alone (they are exported to err "on 123 // the safe side"). 124 got := fmt.Sprint(pkg.Imports()) 125 for _, want := range []string{"go/ast", "go/token"} { 126 if !strings.Contains(got, want) { 127 t.Errorf(`Package("exports").Imports() = %s, does not contain %s`, got, want) 128 } 129 } 130 } 131 } 132 133 // TODO(gri) Remove this function once we switched to new export format by default 134 // (and update the comment and want list in TestImportTestdata). 135 func TestImportTestdataNewExport(t *testing.T) { 136 // This package only handles gc export data. 137 if runtime.Compiler != "gc" { 138 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) 139 return 140 } 141 142 if outFn := compileNewExport(t, "testdata", "exports.go"); outFn != "" { 143 defer os.Remove(outFn) 144 } 145 146 if pkg := testPath(t, "./testdata/exports", "."); pkg != nil { 147 // The package's Imports list must include all packages 148 // explicitly imported by exports.go, plus all packages 149 // referenced indirectly via exported objects in exports.go. 150 want := `[package ast ("go/ast") package token ("go/token")]` 151 got := fmt.Sprint(pkg.Imports()) 152 if got != want { 153 t.Errorf(`Package("exports").Imports() = %s, want %s`, got, want) 154 } 155 } 156 } 157 158 func TestImportStdLib(t *testing.T) { 159 skipSpecialPlatforms(t) 160 161 // This package only handles gc export data. 162 if runtime.Compiler != "gc" { 163 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) 164 return 165 } 166 167 dt := maxTime 168 if testing.Short() && testenv.Builder() == "" { 169 dt = 10 * time.Millisecond 170 } 171 nimports := testDir(t, "", time.Now().Add(dt)) // installed packages 172 t.Logf("tested %d imports", nimports) 173 } 174 175 var importedObjectTests = []struct { 176 name string 177 want string 178 }{ 179 {"math.Pi", "const Pi untyped float"}, 180 {"io.Reader", "type Reader interface{Read(p []byte) (n int, err error)}"}, 181 {"io.ReadWriter", "type ReadWriter interface{Read(p []byte) (n int, err error); Write(p []byte) (n int, err error)}"}, 182 {"math.Sin", "func Sin(x float64) float64"}, 183 // TODO(gri) add more tests 184 } 185 186 func TestImportedTypes(t *testing.T) { 187 skipSpecialPlatforms(t) 188 189 // This package only handles gc export data. 190 if runtime.Compiler != "gc" { 191 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) 192 return 193 } 194 195 for _, test := range importedObjectTests { 196 s := strings.Split(test.name, ".") 197 if len(s) != 2 { 198 t.Fatal("inconsistent test data") 199 } 200 importPath := s[0] 201 objName := s[1] 202 203 pkg, err := Import(make(map[string]*types.Package), importPath, ".") 204 if err != nil { 205 t.Error(err) 206 continue 207 } 208 209 obj := pkg.Scope().Lookup(objName) 210 if obj == nil { 211 t.Errorf("%s: object not found", test.name) 212 continue 213 } 214 215 got := types.ObjectString(obj, types.RelativeTo(pkg)) 216 if got != test.want { 217 t.Errorf("%s: got %q; want %q", test.name, got, test.want) 218 } 219 } 220 } 221 222 func TestIssue5815(t *testing.T) { 223 skipSpecialPlatforms(t) 224 225 // This package only handles gc export data. 226 if runtime.Compiler != "gc" { 227 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) 228 return 229 } 230 231 pkg, err := Import(make(map[string]*types.Package), "strings", ".") 232 if err != nil { 233 t.Fatal(err) 234 } 235 236 scope := pkg.Scope() 237 for _, name := range scope.Names() { 238 obj := scope.Lookup(name) 239 if obj.Pkg() == nil { 240 t.Errorf("no pkg for %s", obj) 241 } 242 if tname, _ := obj.(*types.TypeName); tname != nil { 243 named := tname.Type().(*types.Named) 244 for i := 0; i < named.NumMethods(); i++ { 245 m := named.Method(i) 246 if m.Pkg() == nil { 247 t.Errorf("no pkg for %s", m) 248 } 249 } 250 } 251 } 252 } 253 254 // Smoke test to ensure that imported methods get the correct package. 255 func TestCorrectMethodPackage(t *testing.T) { 256 skipSpecialPlatforms(t) 257 258 // This package only handles gc export data. 259 if runtime.Compiler != "gc" { 260 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) 261 return 262 } 263 264 imports := make(map[string]*types.Package) 265 _, err := Import(imports, "net/http", ".") 266 if err != nil { 267 t.Fatal(err) 268 } 269 270 mutex := imports["sync"].Scope().Lookup("Mutex").(*types.TypeName).Type() 271 mset := types.NewMethodSet(types.NewPointer(mutex)) // methods of *sync.Mutex 272 sel := mset.Lookup(nil, "Lock") 273 lock := sel.Obj().(*types.Func) 274 if got, want := lock.Pkg().Path(), "sync"; got != want { 275 t.Errorf("got package path %q; want %q", got, want) 276 } 277 } 278 279 func TestIssue13566(t *testing.T) { 280 skipSpecialPlatforms(t) 281 282 // This package only handles gc export data. 283 if runtime.Compiler != "gc" { 284 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) 285 return 286 } 287 288 // On windows, we have to set the -D option for the compiler to avoid having a drive 289 // letter and an illegal ':' in the import path - just skip it (see also issue #3483). 290 if runtime.GOOS == "windows" { 291 t.Skip("avoid dealing with relative paths/drive letters on windows") 292 } 293 294 if f := compile(t, "testdata", "a.go"); f != "" { 295 defer os.Remove(f) 296 } 297 if f := compile(t, "testdata", "b.go"); f != "" { 298 defer os.Remove(f) 299 } 300 301 // import must succeed (test for issue at hand) 302 pkg, err := Import(make(map[string]*types.Package), "./testdata/b", ".") 303 if err != nil { 304 t.Fatal(err) 305 } 306 307 // make sure all indirectly imported packages have names 308 for _, imp := range pkg.Imports() { 309 if imp.Name() == "" { 310 t.Errorf("no name for %s package", imp.Path()) 311 } 312 } 313 } 314 315 func TestIssue13898(t *testing.T) { 316 skipSpecialPlatforms(t) 317 318 // This package only handles gc export data. 319 if runtime.Compiler != "gc" { 320 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) 321 return 322 } 323 324 // import go/internal/gcimporter which imports go/types partially 325 imports := make(map[string]*types.Package) 326 _, err := Import(imports, "go/internal/gcimporter", ".") 327 if err != nil { 328 t.Fatal(err) 329 } 330 331 // look for go/types package 332 var goTypesPkg *types.Package 333 for path, pkg := range imports { 334 if path == "go/types" { 335 goTypesPkg = pkg 336 break 337 } 338 } 339 if goTypesPkg == nil { 340 t.Fatal("go/types not found") 341 } 342 343 // look for go/types.Object type 344 obj := goTypesPkg.Scope().Lookup("Object") 345 if obj == nil { 346 t.Fatal("go/types.Object not found") 347 } 348 typ, ok := obj.Type().(*types.Named) 349 if !ok { 350 t.Fatalf("go/types.Object type is %v; wanted named type", typ) 351 } 352 353 // lookup go/types.Object.Pkg method 354 m, index, indirect := types.LookupFieldOrMethod(typ, false, nil, "Pkg") 355 if m == nil { 356 t.Fatalf("go/types.Object.Pkg not found (index = %v, indirect = %v)", index, indirect) 357 } 358 359 // the method must belong to go/types 360 if m.Pkg().Path() != "go/types" { 361 t.Fatalf("found %v; want go/types", m.Pkg()) 362 } 363 } 364 365 func TestIssue15517(t *testing.T) { 366 skipSpecialPlatforms(t) 367 368 // This package only handles gc export data. 369 if runtime.Compiler != "gc" { 370 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) 371 return 372 } 373 374 // On windows, we have to set the -D option for the compiler to avoid having a drive 375 // letter and an illegal ':' in the import path - just skip it (see also issue #3483). 376 if runtime.GOOS == "windows" { 377 t.Skip("avoid dealing with relative paths/drive letters on windows") 378 } 379 380 if f := compile(t, "testdata", "p.go"); f != "" { 381 defer os.Remove(f) 382 } 383 384 // Multiple imports of p must succeed without redeclaration errors. 385 // We use an import path that's not cleaned up so that the eventual 386 // file path for the package is different from the package path; this 387 // will expose the error if it is present. 388 // 389 // (Issue: Both the textual and the binary importer used the file path 390 // of the package to be imported as key into the shared packages map. 391 // However, the binary importer then used the package path to identify 392 // the imported package to mark it as complete; effectively marking the 393 // wrong package as complete. By using an "unclean" package path, the 394 // file and package path are different, exposing the problem if present. 395 // The same issue occurs with vendoring.) 396 imports := make(map[string]*types.Package) 397 for i := 0; i < 3; i++ { 398 if _, err := Import(imports, "./././testdata/p", "."); err != nil { 399 t.Fatal(err) 400 } 401 } 402 } 403 404 func TestIssue15920(t *testing.T) { 405 skipSpecialPlatforms(t) 406 407 // This package only handles gc export data. 408 if runtime.Compiler != "gc" { 409 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) 410 return 411 } 412 413 // On windows, we have to set the -D option for the compiler to avoid having a drive 414 // letter and an illegal ':' in the import path - just skip it (see also issue #3483). 415 if runtime.GOOS == "windows" { 416 t.Skip("avoid dealing with relative paths/drive letters on windows") 417 } 418 419 if f := compile(t, "testdata", "issue15920.go"); f != "" { 420 defer os.Remove(f) 421 } 422 423 imports := make(map[string]*types.Package) 424 if _, err := Import(imports, "./testdata/issue15920", "."); err != nil { 425 t.Fatal(err) 426 } 427 }