github.com/megatontech/mynoteforgo@v0.0.0-20200507084910-5d0c6ea6e890/源码/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 "bytes" 9 "fmt" 10 "internal/testenv" 11 "io/ioutil" 12 "os" 13 "os/exec" 14 "path/filepath" 15 "runtime" 16 "strings" 17 "testing" 18 "time" 19 20 "go/token" 21 "go/types" 22 ) 23 24 // skipSpecialPlatforms causes the test to be skipped for platforms where 25 // builders (build.golang.org) don't have access to compiled packages for 26 // import. 27 func skipSpecialPlatforms(t *testing.T) { 28 switch platform := runtime.GOOS + "-" + runtime.GOARCH; platform { 29 case "nacl-amd64p32", 30 "nacl-386", 31 "nacl-arm", 32 "darwin-arm", 33 "darwin-arm64": 34 t.Skipf("no compiled packages available for import on %s", platform) 35 } 36 } 37 38 // compile runs the compiler on filename, with dirname as the working directory, 39 // and writes the output file to outdirname. 40 func compile(t *testing.T, dirname, filename, outdirname string) string { 41 // filename must end with ".go" 42 if !strings.HasSuffix(filename, ".go") { 43 t.Fatalf("filename doesn't end in .go: %s", filename) 44 } 45 basename := filepath.Base(filename) 46 outname := filepath.Join(outdirname, basename[:len(basename)-2]+"o") 47 cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "-o", outname, filename) 48 cmd.Dir = dirname 49 out, err := cmd.CombinedOutput() 50 if err != nil { 51 t.Logf("%s", out) 52 t.Fatalf("go tool compile %s failed: %s", filename, err) 53 } 54 return outname 55 } 56 57 func testPath(t *testing.T, path, srcDir string) *types.Package { 58 t0 := time.Now() 59 fset := token.NewFileSet() 60 pkg, err := Import(fset, make(map[string]*types.Package), path, srcDir, nil) 61 if err != nil { 62 t.Errorf("testPath(%s): %s", path, err) 63 return nil 64 } 65 t.Logf("testPath(%s): %v", path, time.Since(t0)) 66 return pkg 67 } 68 69 const maxTime = 30 * time.Second 70 71 func testDir(t *testing.T, dir string, endTime time.Time) (nimports int) { 72 dirname := filepath.Join(runtime.GOROOT(), "pkg", runtime.GOOS+"_"+runtime.GOARCH, dir) 73 list, err := ioutil.ReadDir(dirname) 74 if err != nil { 75 t.Fatalf("testDir(%s): %s", dirname, err) 76 } 77 for _, f := range list { 78 if time.Now().After(endTime) { 79 t.Log("testing time used up") 80 return 81 } 82 switch { 83 case !f.IsDir(): 84 // try extensions 85 for _, ext := range pkgExts { 86 if strings.HasSuffix(f.Name(), ext) { 87 name := f.Name()[0 : len(f.Name())-len(ext)] // remove extension 88 if testPath(t, filepath.Join(dir, name), dir) != nil { 89 nimports++ 90 } 91 } 92 } 93 case f.IsDir(): 94 nimports += testDir(t, filepath.Join(dir, f.Name()), endTime) 95 } 96 } 97 return 98 } 99 100 func mktmpdir(t *testing.T) string { 101 tmpdir, err := ioutil.TempDir("", "gcimporter_test") 102 if err != nil { 103 t.Fatal("mktmpdir:", err) 104 } 105 if err := os.Mkdir(filepath.Join(tmpdir, "testdata"), 0700); err != nil { 106 os.RemoveAll(tmpdir) 107 t.Fatal("mktmpdir:", err) 108 } 109 return tmpdir 110 } 111 112 func TestImportTestdata(t *testing.T) { 113 // This package only handles gc export data. 114 if runtime.Compiler != "gc" { 115 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) 116 } 117 118 tmpdir := mktmpdir(t) 119 defer os.RemoveAll(tmpdir) 120 121 compile(t, "testdata", "exports.go", filepath.Join(tmpdir, "testdata")) 122 123 if pkg := testPath(t, "./testdata/exports", tmpdir); pkg != nil { 124 // The package's Imports list must include all packages 125 // explicitly imported by exports.go, plus all packages 126 // referenced indirectly via exported objects in exports.go. 127 // With the textual export format, the list may also include 128 // additional packages that are not strictly required for 129 // import processing alone (they are exported to err "on 130 // the safe side"). 131 // TODO(gri) update the want list to be precise, now that 132 // the textual export data is gone. 133 got := fmt.Sprint(pkg.Imports()) 134 for _, want := range []string{"go/ast", "go/token"} { 135 if !strings.Contains(got, want) { 136 t.Errorf(`Package("exports").Imports() = %s, does not contain %s`, got, want) 137 } 138 } 139 } 140 } 141 142 func TestVersionHandling(t *testing.T) { 143 skipSpecialPlatforms(t) // we really only need to exclude nacl platforms, but this is fine 144 145 // This package only handles gc export data. 146 if runtime.Compiler != "gc" { 147 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) 148 } 149 150 const dir = "./testdata/versions" 151 list, err := ioutil.ReadDir(dir) 152 if err != nil { 153 t.Fatal(err) 154 } 155 156 tmpdir := mktmpdir(t) 157 defer os.RemoveAll(tmpdir) 158 corruptdir := filepath.Join(tmpdir, "testdata", "versions") 159 if err := os.Mkdir(corruptdir, 0700); err != nil { 160 t.Fatal(err) 161 } 162 163 fset := token.NewFileSet() 164 165 for _, f := range list { 166 name := f.Name() 167 if !strings.HasSuffix(name, ".a") { 168 continue // not a package file 169 } 170 if strings.Contains(name, "corrupted") { 171 continue // don't process a leftover corrupted file 172 } 173 pkgpath := "./" + name[:len(name)-2] 174 175 if testing.Verbose() { 176 t.Logf("importing %s", name) 177 } 178 179 // test that export data can be imported 180 _, err := Import(fset, make(map[string]*types.Package), pkgpath, dir, nil) 181 if err != nil { 182 // ok to fail if it fails with a newer version error for select files 183 if strings.Contains(err.Error(), "newer version") { 184 switch name { 185 case "test_go1.11_999b.a", "test_go1.11_999i.a": 186 continue 187 } 188 // fall through 189 } 190 t.Errorf("import %q failed: %v", pkgpath, err) 191 continue 192 } 193 194 // create file with corrupted export data 195 // 1) read file 196 data, err := ioutil.ReadFile(filepath.Join(dir, name)) 197 if err != nil { 198 t.Fatal(err) 199 } 200 // 2) find export data 201 i := bytes.Index(data, []byte("\n$$B\n")) + 5 202 j := bytes.Index(data[i:], []byte("\n$$\n")) + i 203 if i < 0 || j < 0 || i > j { 204 t.Fatalf("export data section not found (i = %d, j = %d)", i, j) 205 } 206 // 3) corrupt the data (increment every 7th byte) 207 for k := j - 13; k >= i; k -= 7 { 208 data[k]++ 209 } 210 // 4) write the file 211 pkgpath += "_corrupted" 212 filename := filepath.Join(corruptdir, pkgpath) + ".a" 213 ioutil.WriteFile(filename, data, 0666) 214 215 // test that importing the corrupted file results in an error 216 _, err = Import(fset, make(map[string]*types.Package), pkgpath, corruptdir, nil) 217 if err == nil { 218 t.Errorf("import corrupted %q succeeded", pkgpath) 219 } else if msg := err.Error(); !strings.Contains(msg, "version skew") { 220 t.Errorf("import %q error incorrect (%s)", pkgpath, msg) 221 } 222 } 223 } 224 225 func TestImportStdLib(t *testing.T) { 226 skipSpecialPlatforms(t) 227 228 // This package only handles gc export data. 229 if runtime.Compiler != "gc" { 230 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) 231 } 232 233 dt := maxTime 234 if testing.Short() && testenv.Builder() == "" { 235 dt = 10 * time.Millisecond 236 } 237 nimports := testDir(t, "", time.Now().Add(dt)) // installed packages 238 t.Logf("tested %d imports", nimports) 239 } 240 241 var importedObjectTests = []struct { 242 name string 243 want string 244 }{ 245 // non-interfaces 246 {"crypto.Hash", "type Hash uint"}, 247 {"go/ast.ObjKind", "type ObjKind int"}, 248 {"go/types.Qualifier", "type Qualifier func(*Package) string"}, 249 {"go/types.Comparable", "func Comparable(T Type) bool"}, 250 {"math.Pi", "const Pi untyped float"}, 251 {"math.Sin", "func Sin(x float64) float64"}, 252 {"go/ast.NotNilFilter", "func NotNilFilter(_ string, v reflect.Value) bool"}, 253 {"go/internal/gcimporter.BImportData", "func BImportData(fset *go/token.FileSet, imports map[string]*go/types.Package, data []byte, path string) (_ int, pkg *go/types.Package, err error)"}, 254 255 // interfaces 256 {"context.Context", "type Context interface{Deadline() (deadline time.Time, ok bool); Done() <-chan struct{}; Err() error; Value(key interface{}) interface{}}"}, 257 {"crypto.Decrypter", "type Decrypter interface{Decrypt(rand io.Reader, msg []byte, opts DecrypterOpts) (plaintext []byte, err error); Public() PublicKey}"}, 258 {"encoding.BinaryMarshaler", "type BinaryMarshaler interface{MarshalBinary() (data []byte, err error)}"}, 259 {"io.Reader", "type Reader interface{Read(p []byte) (n int, err error)}"}, 260 {"io.ReadWriter", "type ReadWriter interface{Reader; Writer}"}, 261 {"go/ast.Node", "type Node interface{End() go/token.Pos; Pos() go/token.Pos}"}, 262 {"go/types.Type", "type Type interface{String() string; Underlying() Type}"}, 263 } 264 265 func TestImportedTypes(t *testing.T) { 266 skipSpecialPlatforms(t) 267 268 // This package only handles gc export data. 269 if runtime.Compiler != "gc" { 270 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) 271 } 272 273 fset := token.NewFileSet() 274 for _, test := range importedObjectTests { 275 s := strings.Split(test.name, ".") 276 if len(s) != 2 { 277 t.Fatal("inconsistent test data") 278 } 279 importPath := s[0] 280 objName := s[1] 281 282 pkg, err := Import(fset, make(map[string]*types.Package), importPath, ".", nil) 283 if err != nil { 284 t.Error(err) 285 continue 286 } 287 288 obj := pkg.Scope().Lookup(objName) 289 if obj == nil { 290 t.Errorf("%s: object not found", test.name) 291 continue 292 } 293 294 got := types.ObjectString(obj, types.RelativeTo(pkg)) 295 if got != test.want { 296 t.Errorf("%s: got %q; want %q", test.name, got, test.want) 297 } 298 299 if named, _ := obj.Type().(*types.Named); named != nil { 300 verifyInterfaceMethodRecvs(t, named, 0) 301 } 302 } 303 } 304 305 // verifyInterfaceMethodRecvs verifies that method receiver types 306 // are named if the methods belong to a named interface type. 307 func verifyInterfaceMethodRecvs(t *testing.T, named *types.Named, level int) { 308 // avoid endless recursion in case of an embedding bug that lead to a cycle 309 if level > 10 { 310 t.Errorf("%s: embeds itself", named) 311 return 312 } 313 314 iface, _ := named.Underlying().(*types.Interface) 315 if iface == nil { 316 return // not an interface 317 } 318 319 // check explicitly declared methods 320 for i := 0; i < iface.NumExplicitMethods(); i++ { 321 m := iface.ExplicitMethod(i) 322 recv := m.Type().(*types.Signature).Recv() 323 if recv == nil { 324 t.Errorf("%s: missing receiver type", m) 325 continue 326 } 327 if recv.Type() != named { 328 t.Errorf("%s: got recv type %s; want %s", m, recv.Type(), named) 329 } 330 } 331 332 // check embedded interfaces (if they are named, too) 333 for i := 0; i < iface.NumEmbeddeds(); i++ { 334 // embedding of interfaces cannot have cycles; recursion will terminate 335 if etype, _ := iface.EmbeddedType(i).(*types.Named); etype != nil { 336 verifyInterfaceMethodRecvs(t, etype, level+1) 337 } 338 } 339 } 340 341 func TestIssue5815(t *testing.T) { 342 skipSpecialPlatforms(t) 343 344 // This package only handles gc export data. 345 if runtime.Compiler != "gc" { 346 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) 347 } 348 349 pkg := importPkg(t, "strings", ".") 350 351 scope := pkg.Scope() 352 for _, name := range scope.Names() { 353 obj := scope.Lookup(name) 354 if obj.Pkg() == nil { 355 t.Errorf("no pkg for %s", obj) 356 } 357 if tname, _ := obj.(*types.TypeName); tname != nil { 358 named := tname.Type().(*types.Named) 359 for i := 0; i < named.NumMethods(); i++ { 360 m := named.Method(i) 361 if m.Pkg() == nil { 362 t.Errorf("no pkg for %s", m) 363 } 364 } 365 } 366 } 367 } 368 369 // Smoke test to ensure that imported methods get the correct package. 370 func TestCorrectMethodPackage(t *testing.T) { 371 skipSpecialPlatforms(t) 372 373 // This package only handles gc export data. 374 if runtime.Compiler != "gc" { 375 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) 376 } 377 378 imports := make(map[string]*types.Package) 379 fset := token.NewFileSet() 380 _, err := Import(fset, imports, "net/http", ".", nil) 381 if err != nil { 382 t.Fatal(err) 383 } 384 385 mutex := imports["sync"].Scope().Lookup("Mutex").(*types.TypeName).Type() 386 mset := types.NewMethodSet(types.NewPointer(mutex)) // methods of *sync.Mutex 387 sel := mset.Lookup(nil, "Lock") 388 lock := sel.Obj().(*types.Func) 389 if got, want := lock.Pkg().Path(), "sync"; got != want { 390 t.Errorf("got package path %q; want %q", got, want) 391 } 392 } 393 394 func TestIssue13566(t *testing.T) { 395 skipSpecialPlatforms(t) 396 397 // This package only handles gc export data. 398 if runtime.Compiler != "gc" { 399 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) 400 } 401 402 // On windows, we have to set the -D option for the compiler to avoid having a drive 403 // letter and an illegal ':' in the import path - just skip it (see also issue #3483). 404 if runtime.GOOS == "windows" { 405 t.Skip("avoid dealing with relative paths/drive letters on windows") 406 } 407 408 tmpdir := mktmpdir(t) 409 defer os.RemoveAll(tmpdir) 410 testoutdir := filepath.Join(tmpdir, "testdata") 411 412 // b.go needs to be compiled from the output directory so that the compiler can 413 // find the compiled package a. We pass the full path to compile() so that we 414 // don't have to copy the file to that directory. 415 bpath, err := filepath.Abs(filepath.Join("testdata", "b.go")) 416 if err != nil { 417 t.Fatal(err) 418 } 419 compile(t, "testdata", "a.go", testoutdir) 420 compile(t, testoutdir, bpath, testoutdir) 421 422 // import must succeed (test for issue at hand) 423 pkg := importPkg(t, "./testdata/b", tmpdir) 424 425 // make sure all indirectly imported packages have names 426 for _, imp := range pkg.Imports() { 427 if imp.Name() == "" { 428 t.Errorf("no name for %s package", imp.Path()) 429 } 430 } 431 } 432 433 func TestIssue13898(t *testing.T) { 434 skipSpecialPlatforms(t) 435 436 // This package only handles gc export data. 437 if runtime.Compiler != "gc" { 438 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) 439 } 440 441 // import go/internal/gcimporter which imports go/types partially 442 fset := token.NewFileSet() 443 imports := make(map[string]*types.Package) 444 _, err := Import(fset, imports, "go/internal/gcimporter", ".", nil) 445 if err != nil { 446 t.Fatal(err) 447 } 448 449 // look for go/types package 450 var goTypesPkg *types.Package 451 for path, pkg := range imports { 452 if path == "go/types" { 453 goTypesPkg = pkg 454 break 455 } 456 } 457 if goTypesPkg == nil { 458 t.Fatal("go/types not found") 459 } 460 461 // look for go/types.Object type 462 obj := lookupObj(t, goTypesPkg.Scope(), "Object") 463 typ, ok := obj.Type().(*types.Named) 464 if !ok { 465 t.Fatalf("go/types.Object type is %v; wanted named type", typ) 466 } 467 468 // lookup go/types.Object.Pkg method 469 m, index, indirect := types.LookupFieldOrMethod(typ, false, nil, "Pkg") 470 if m == nil { 471 t.Fatalf("go/types.Object.Pkg not found (index = %v, indirect = %v)", index, indirect) 472 } 473 474 // the method must belong to go/types 475 if m.Pkg().Path() != "go/types" { 476 t.Fatalf("found %v; want go/types", m.Pkg()) 477 } 478 } 479 480 func TestIssue15517(t *testing.T) { 481 skipSpecialPlatforms(t) 482 483 // This package only handles gc export data. 484 if runtime.Compiler != "gc" { 485 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) 486 } 487 488 // On windows, we have to set the -D option for the compiler to avoid having a drive 489 // letter and an illegal ':' in the import path - just skip it (see also issue #3483). 490 if runtime.GOOS == "windows" { 491 t.Skip("avoid dealing with relative paths/drive letters on windows") 492 } 493 494 tmpdir := mktmpdir(t) 495 defer os.RemoveAll(tmpdir) 496 497 compile(t, "testdata", "p.go", filepath.Join(tmpdir, "testdata")) 498 499 // Multiple imports of p must succeed without redeclaration errors. 500 // We use an import path that's not cleaned up so that the eventual 501 // file path for the package is different from the package path; this 502 // will expose the error if it is present. 503 // 504 // (Issue: Both the textual and the binary importer used the file path 505 // of the package to be imported as key into the shared packages map. 506 // However, the binary importer then used the package path to identify 507 // the imported package to mark it as complete; effectively marking the 508 // wrong package as complete. By using an "unclean" package path, the 509 // file and package path are different, exposing the problem if present. 510 // The same issue occurs with vendoring.) 511 imports := make(map[string]*types.Package) 512 fset := token.NewFileSet() 513 for i := 0; i < 3; i++ { 514 if _, err := Import(fset, imports, "./././testdata/p", tmpdir, nil); err != nil { 515 t.Fatal(err) 516 } 517 } 518 } 519 520 func TestIssue15920(t *testing.T) { 521 skipSpecialPlatforms(t) 522 523 // This package only handles gc export data. 524 if runtime.Compiler != "gc" { 525 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) 526 } 527 528 // On windows, we have to set the -D option for the compiler to avoid having a drive 529 // letter and an illegal ':' in the import path - just skip it (see also issue #3483). 530 if runtime.GOOS == "windows" { 531 t.Skip("avoid dealing with relative paths/drive letters on windows") 532 } 533 534 compileAndImportPkg(t, "issue15920") 535 } 536 537 func TestIssue20046(t *testing.T) { 538 skipSpecialPlatforms(t) 539 540 // This package only handles gc export data. 541 if runtime.Compiler != "gc" { 542 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) 543 } 544 545 // On windows, we have to set the -D option for the compiler to avoid having a drive 546 // letter and an illegal ':' in the import path - just skip it (see also issue #3483). 547 if runtime.GOOS == "windows" { 548 t.Skip("avoid dealing with relative paths/drive letters on windows") 549 } 550 551 // "./issue20046".V.M must exist 552 pkg := compileAndImportPkg(t, "issue20046") 553 obj := lookupObj(t, pkg.Scope(), "V") 554 if m, index, indirect := types.LookupFieldOrMethod(obj.Type(), false, nil, "M"); m == nil { 555 t.Fatalf("V.M not found (index = %v, indirect = %v)", index, indirect) 556 } 557 } 558 func TestIssue25301(t *testing.T) { 559 skipSpecialPlatforms(t) 560 561 // This package only handles gc export data. 562 if runtime.Compiler != "gc" { 563 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) 564 } 565 566 // On windows, we have to set the -D option for the compiler to avoid having a drive 567 // letter and an illegal ':' in the import path - just skip it (see also issue #3483). 568 if runtime.GOOS == "windows" { 569 t.Skip("avoid dealing with relative paths/drive letters on windows") 570 } 571 572 compileAndImportPkg(t, "issue25301") 573 } 574 575 func TestIssue25596(t *testing.T) { 576 skipSpecialPlatforms(t) 577 578 // This package only handles gc export data. 579 if runtime.Compiler != "gc" { 580 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) 581 } 582 583 // On windows, we have to set the -D option for the compiler to avoid having a drive 584 // letter and an illegal ':' in the import path - just skip it (see also issue #3483). 585 if runtime.GOOS == "windows" { 586 t.Skip("avoid dealing with relative paths/drive letters on windows") 587 } 588 589 compileAndImportPkg(t, "issue25596") 590 } 591 592 func importPkg(t *testing.T, path, srcDir string) *types.Package { 593 fset := token.NewFileSet() 594 pkg, err := Import(fset, make(map[string]*types.Package), path, srcDir, nil) 595 if err != nil { 596 t.Fatal(err) 597 } 598 return pkg 599 } 600 601 func compileAndImportPkg(t *testing.T, name string) *types.Package { 602 tmpdir := mktmpdir(t) 603 defer os.RemoveAll(tmpdir) 604 compile(t, "testdata", name+".go", filepath.Join(tmpdir, "testdata")) 605 return importPkg(t, "./testdata/"+name, tmpdir) 606 } 607 608 func lookupObj(t *testing.T, scope *types.Scope, name string) types.Object { 609 if obj := scope.Lookup(name); obj != nil { 610 return obj 611 } 612 t.Fatalf("%s not found", name) 613 return nil 614 }