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