github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/tools/go/importer/import_test.go (about) 1 // Copyright 2013 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 importer 6 7 import ( 8 "bufio" 9 "bytes" 10 "fmt" 11 "go/ast" 12 "go/build" 13 "go/parser" 14 "go/token" 15 "os" 16 "path/filepath" 17 "runtime" 18 "sort" 19 "strconv" 20 "testing" 21 "time" 22 23 "golang.org/x/tools/go/gcimporter" 24 "golang.org/x/tools/go/types" 25 ) 26 27 var fset = token.NewFileSet() 28 29 var tests = []string{ 30 `package p`, 31 32 // consts 33 `package p; const X = true`, 34 `package p; const X, y, Z = true, false, 0 != 0`, 35 `package p; const ( A float32 = 1<<iota; B; C; D)`, 36 `package p; const X = "foo"`, 37 `package p; const X string = "foo"`, 38 `package p; const X = 0`, 39 `package p; const X = -42`, 40 `package p; const X = 3.14159265`, 41 `package p; const X = -1e-10`, 42 `package p; const X = 1.2 + 2.3i`, 43 `package p; const X = -1i`, 44 `package p; import "math"; const Pi = math.Pi`, 45 `package p; import m "math"; const Pi = m.Pi`, 46 47 // types 48 `package p; type T int`, 49 `package p; type T [10]int`, 50 `package p; type T []int`, 51 `package p; type T struct{}`, 52 `package p; type T struct{x int}`, 53 `package p; type T *int`, 54 `package p; type T func()`, 55 `package p; type T *T`, 56 `package p; type T interface{}`, 57 `package p; type T interface{ foo() }`, 58 `package p; type T interface{ m() T }`, 59 // TODO(gri) disabled for now - import/export works but 60 // types.Type.String() used in the test cannot handle cases 61 // like this yet 62 // `package p; type T interface{ m() interface{T} }`, 63 `package p; type T map[string]bool`, 64 `package p; type T chan int`, 65 `package p; type T <-chan complex64`, 66 `package p; type T chan<- map[int]string`, 67 // test case for issue 8177 68 `package p; type T1 interface { F(T2) }; type T2 interface { T1 }`, 69 70 // vars 71 `package p; var X int`, 72 `package p; var X, Y, Z struct{f int "tag"}`, 73 74 // funcs 75 `package p; func F()`, 76 `package p; func F(x int, y struct{}) bool`, 77 `package p; type T int; func (*T) F(x int, y struct{}) T`, 78 79 // selected special cases 80 `package p; type T int`, 81 `package p; type T uint8`, 82 `package p; type T byte`, 83 `package p; type T error`, 84 `package p; import "net/http"; type T http.Client`, 85 `package p; import "net/http"; type ( T1 http.Client; T2 struct { http.Client } )`, 86 `package p; import "unsafe"; type ( T1 unsafe.Pointer; T2 unsafe.Pointer )`, 87 `package p; import "unsafe"; type T struct { p unsafe.Pointer }`, 88 } 89 90 func TestImportSrc(t *testing.T) { 91 for _, src := range tests { 92 pkg, err := pkgForSource(src) 93 if err != nil { 94 t.Errorf("typecheck failed: %s", err) 95 continue 96 } 97 testExportImport(t, pkg, "") 98 } 99 } 100 101 func TestImportStdLib(t *testing.T) { 102 start := time.Now() 103 104 libs, err := stdLibs() 105 if err != nil { 106 t.Fatalf("could not compute list of std libraries: %s", err) 107 } 108 if len(libs) < 100 { 109 t.Fatalf("only %d std libraries found - something's not right", len(libs)) 110 } 111 112 // make sure printed go/types types and gc-imported types 113 // can be compared reasonably well 114 types.GcCompatibilityMode = true 115 116 var totSize, totGcSize int 117 for _, lib := range libs { 118 // limit run time for short tests 119 if testing.Short() && time.Since(start) >= 750*time.Millisecond { 120 return 121 } 122 if lib == "cmd/internal/objfile" || lib == "net/http" { 123 // gcimporter doesn't support vendored imports. 124 // TODO(gri): fix. 125 continue 126 } 127 128 pkg, err := pkgForPath(lib) 129 switch err := err.(type) { 130 case nil: 131 // ok 132 case *build.NoGoError: 133 // no Go files - ignore 134 continue 135 default: 136 t.Errorf("typecheck failed: %s", err) 137 continue 138 } 139 140 size, gcsize := testExportImport(t, pkg, lib) 141 if gcsize == 0 { 142 // if gc import didn't happen, assume same size 143 // (and avoid division by zero below) 144 gcsize = size 145 } 146 147 if testing.Verbose() { 148 fmt.Printf("%s\t%d\t%d\t%d%%\n", lib, size, gcsize, int(float64(size)*100/float64(gcsize))) 149 } 150 totSize += size 151 totGcSize += gcsize 152 } 153 154 if testing.Verbose() { 155 fmt.Printf("\n%d\t%d\t%d%%\n", totSize, totGcSize, int(float64(totSize)*100/float64(totGcSize))) 156 } 157 158 types.GcCompatibilityMode = false 159 } 160 161 func testExportImport(t *testing.T, pkg0 *types.Package, path string) (size, gcsize int) { 162 data := ExportData(pkg0) 163 size = len(data) 164 165 imports := make(map[string]*types.Package) 166 n, pkg1, err := ImportData(imports, data) 167 if err != nil { 168 t.Errorf("package %s: import failed: %s", pkg0.Name(), err) 169 return 170 } 171 if n != size { 172 t.Errorf("package %s: not all input data consumed", pkg0.Name()) 173 return 174 } 175 176 s0 := pkgString(pkg0) 177 s1 := pkgString(pkg1) 178 if s1 != s0 { 179 t.Errorf("package %s: \nimport got:\n%s\nwant:\n%s\n", pkg0.Name(), s1, s0) 180 } 181 182 // If we have a standard library, compare also against the gcimported package. 183 if path == "" { 184 return // not std library 185 } 186 187 gcdata, err := gcExportData(path) 188 if err != nil { 189 if pkg0.Name() == "main" { 190 return // no export data present for main package 191 } 192 t.Errorf("package %s: couldn't get export data: %s", pkg0.Name(), err) 193 } 194 gcsize = len(gcdata) 195 196 imports = make(map[string]*types.Package) 197 pkg2, err := gcImportData(imports, gcdata, path) 198 if err != nil { 199 t.Errorf("package %s: gcimport failed: %s", pkg0.Name(), err) 200 return 201 } 202 203 s2 := pkgString(pkg2) 204 if s2 != s0 { 205 t.Errorf("package %s: \ngcimport got:\n%s\nwant:\n%s\n", pkg0.Name(), s2, s0) 206 } 207 208 return 209 } 210 211 func pkgForSource(src string) (*types.Package, error) { 212 f, err := parser.ParseFile(fset, "", src, 0) 213 if err != nil { 214 return nil, err 215 } 216 return typecheck("import-test", f) 217 } 218 219 func pkgForPath(path string) (*types.Package, error) { 220 // collect filenames 221 ctxt := build.Default 222 pkginfo, err := ctxt.Import(path, "", 0) 223 if err != nil { 224 return nil, err 225 } 226 filenames := append(pkginfo.GoFiles, pkginfo.CgoFiles...) 227 228 // parse files 229 files := make([]*ast.File, len(filenames)) 230 for i, filename := range filenames { 231 var err error 232 files[i], err = parser.ParseFile(fset, filepath.Join(pkginfo.Dir, filename), nil, 0) 233 if err != nil { 234 return nil, err 235 } 236 } 237 238 return typecheck(path, files...) 239 } 240 241 var defaultConf = types.Config{ 242 // we only care about exports and thus can ignore function bodies 243 IgnoreFuncBodies: true, 244 // work around C imports if possible 245 FakeImportC: true, 246 // strconv exports IntSize as a constant. The type-checker must 247 // use the same word size otherwise the result of the type-checker 248 // and gc imports is different. We don't care about alignment 249 // since none of the tests have exported constants depending 250 // on alignment (see also issue 8366). 251 Sizes: &types.StdSizes{WordSize: strconv.IntSize / 8, MaxAlign: 8}, 252 } 253 254 func typecheck(path string, files ...*ast.File) (*types.Package, error) { 255 return defaultConf.Check(path, fset, files, nil) 256 } 257 258 // pkgString returns a string representation of a package's exported interface. 259 func pkgString(pkg *types.Package) string { 260 var buf bytes.Buffer 261 262 fmt.Fprintf(&buf, "package %s\n", pkg.Name()) 263 264 scope := pkg.Scope() 265 for _, name := range scope.Names() { 266 if exported(name) { 267 obj := scope.Lookup(name) 268 buf.WriteString(obj.String()) 269 270 switch obj := obj.(type) { 271 case *types.Const: 272 // For now only print constant values if they are not float 273 // or complex. This permits comparing go/types results with 274 // gc-generated gcimported package interfaces. 275 info := obj.Type().Underlying().(*types.Basic).Info() 276 if info&types.IsFloat == 0 && info&types.IsComplex == 0 { 277 fmt.Fprintf(&buf, " = %s", obj.Val()) 278 } 279 280 case *types.TypeName: 281 // Print associated methods. 282 // Basic types (e.g., unsafe.Pointer) have *types.Basic 283 // type rather than *types.Named; so we need to check. 284 if typ, _ := obj.Type().(*types.Named); typ != nil { 285 if n := typ.NumMethods(); n > 0 { 286 // Sort methods by name so that we get the 287 // same order independent of whether the 288 // methods got imported or coming directly 289 // for the source. 290 // TODO(gri) This should probably be done 291 // in go/types. 292 list := make([]*types.Func, n) 293 for i := 0; i < n; i++ { 294 list[i] = typ.Method(i) 295 } 296 sort.Sort(byName(list)) 297 298 buf.WriteString("\nmethods (\n") 299 for _, m := range list { 300 fmt.Fprintf(&buf, "\t%s\n", m) 301 } 302 buf.WriteString(")") 303 } 304 } 305 } 306 buf.WriteByte('\n') 307 } 308 } 309 310 return buf.String() 311 } 312 313 var stdLibRoot = filepath.Join(runtime.GOROOT(), "src") + string(filepath.Separator) 314 315 // The following std libraries are excluded from the stdLibs list. 316 var excluded = map[string]bool{ 317 "builtin": true, // contains type declarations with cycles 318 "unsafe": true, // contains fake declarations 319 } 320 321 // stdLibs returns the list of standard library package paths. 322 func stdLibs() (list []string, err error) { 323 err = filepath.Walk(stdLibRoot, func(path string, info os.FileInfo, err error) error { 324 if err == nil && info.IsDir() { 325 // testdata directories don't contain importable libraries 326 if info.Name() == "testdata" { 327 return filepath.SkipDir 328 } 329 pkgPath := path[len(stdLibRoot):] // remove stdLibRoot 330 if len(pkgPath) > 0 && !excluded[pkgPath] { 331 list = append(list, pkgPath) 332 } 333 } 334 return nil 335 }) 336 return 337 } 338 339 type byName []*types.Func 340 341 func (a byName) Len() int { return len(a) } 342 func (a byName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 343 func (a byName) Less(i, j int) bool { return a[i].Name() < a[j].Name() } 344 345 // gcExportData returns the gc-generated export data for the given path. 346 // It is based on a trimmed-down version of gcimporter.Import which does 347 // not do the actual import, does not handle package unsafe, and assumes 348 // that path is a correct standard library package path (no canonicalization, 349 // or handling of local import paths). 350 func gcExportData(path string) ([]byte, error) { 351 filename, id := gcimporter.FindPkg(path, "") 352 if filename == "" { 353 return nil, fmt.Errorf("can't find import: %s", path) 354 } 355 if id != path { 356 panic("path should be canonicalized") 357 } 358 359 f, err := os.Open(filename) 360 if err != nil { 361 return nil, err 362 } 363 defer f.Close() 364 365 buf := bufio.NewReader(f) 366 if err = gcimporter.FindExportData(buf); err != nil { 367 return nil, err 368 } 369 370 var data []byte 371 for { 372 line, err := buf.ReadBytes('\n') 373 if err != nil { 374 return nil, err 375 } 376 data = append(data, line...) 377 // export data ends in "$$\n" 378 if len(line) == 3 && line[0] == '$' && line[1] == '$' { 379 return data, nil 380 } 381 } 382 } 383 384 func gcImportData(imports map[string]*types.Package, data []byte, path string) (*types.Package, error) { 385 filename := fmt.Sprintf("<filename for %s>", path) // so we have a decent error message if necessary 386 return gcimporter.ImportData(imports, filename, path, bufio.NewReader(bytes.NewBuffer(data))) 387 }