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