1// Copyright 2018 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 5package packages_test 6 7import ( 8 "bytes" 9 "io/ioutil" 10 "path/filepath" 11 "runtime" 12 "strings" 13 "testing" 14 "time" 15 16 "golang.org/x/tools/go/packages" 17 "golang.org/x/tools/internal/testenv" 18) 19 20// This test loads the metadata for the standard library, 21func TestStdlibMetadata(t *testing.T) { 22 // TODO(adonovan): see if we can get away without this hack. 23 // if runtime.GOOS == "android" { 24 // t.Skipf("incomplete std lib on %s", runtime.GOOS) 25 // } 26 27 testenv.NeedsGoPackages(t) 28 29 runtime.GC() 30 t0 := time.Now() 31 var memstats runtime.MemStats 32 runtime.ReadMemStats(&memstats) 33 alloc := memstats.Alloc 34 35 // Load, parse and type-check the program. 36 cfg := &packages.Config{Mode: packages.LoadAllSyntax} 37 pkgs, err := packages.Load(cfg, "std") 38 if err != nil { 39 t.Fatalf("failed to load metadata: %v", err) 40 } 41 if packages.PrintErrors(pkgs) > 0 { 42 t.Fatal("there were errors loading standard library") 43 } 44 45 t1 := time.Now() 46 runtime.GC() 47 runtime.ReadMemStats(&memstats) 48 runtime.KeepAlive(pkgs) 49 50 t.Logf("Loaded %d packages", len(pkgs)) 51 numPkgs := len(pkgs) 52 53 want := 150 // 186 on linux, 185 on windows. 54 if numPkgs < want { 55 t.Errorf("Loaded only %d packages, want at least %d", numPkgs, want) 56 } 57 58 t.Log("GOMAXPROCS: ", runtime.GOMAXPROCS(0)) 59 t.Log("Metadata: ", t1.Sub(t0)) // ~800ms on 12 threads 60 t.Log("#MB: ", int64(memstats.Alloc-alloc)/1000000) // ~1MB 61} 62 63func TestCgoOption(t *testing.T) { 64 if testing.Short() { 65 t.Skip("skipping in short mode; uses tons of memory (https://golang.org/issue/14113)") 66 } 67 68 testenv.NeedsGoPackages(t) 69 70 // TODO(adonovan): see if we can get away without these old 71 // go/loader hacks now that we use the go list command. 72 // 73 // switch runtime.GOOS { 74 // // On these systems, the net and os/user packages don't use cgo 75 // // or the std library is incomplete (Android). 76 // case "android", "plan9", "solaris", "windows": 77 // t.Skipf("no cgo or incomplete std lib on %s", runtime.GOOS) 78 // } 79 // // In nocgo builds (e.g. linux-amd64-nocgo), 80 // // there is no "runtime/cgo" package, 81 // // so cgo-generated Go files will have a failing import. 82 // if !build.Default.CgoEnabled { 83 // return 84 // } 85 86 // Test that we can load cgo-using packages with 87 // DisableCgo=true/false, which, among other things, causes go 88 // list to select pure Go/native implementations, respectively, 89 // based on build tags. 90 // 91 // Each entry specifies a package-level object and the generic 92 // file expected to define it when cgo is disabled. 93 // When cgo is enabled, the exact file is not specified (since 94 // it varies by platform), but must differ from the generic one. 95 // 96 // The test also loads the actual file to verify that the 97 // object is indeed defined at that location. 98 for _, test := range []struct { 99 pkg, declKeyword, name, genericFile string 100 }{ 101 {"net", "type", "addrinfoErrno", "cgo_stub.go"}, 102 {"os/user", "func", "current", "lookup_stubs.go"}, 103 } { 104 cfg := &packages.Config{Mode: packages.LoadSyntax} 105 pkgs, err := packages.Load(cfg, test.pkg) 106 if err != nil { 107 t.Errorf("Load failed: %v", err) 108 continue 109 } 110 if packages.PrintErrors(pkgs) > 0 { 111 t.Error("there were errors loading standard library") 112 continue 113 } 114 pkg := pkgs[0] 115 obj := pkg.Types.Scope().Lookup(test.name) 116 if obj == nil { 117 t.Errorf("no object %s.%s", test.pkg, test.name) 118 continue 119 } 120 posn := pkg.Fset.Position(obj.Pos()) 121 gotFile := filepath.Base(posn.Filename) 122 filesMatch := gotFile == test.genericFile 123 124 if filesMatch { 125 t.Errorf("!DisableCgo: %s found in %s, want native file", 126 obj, gotFile) 127 } 128 129 // Load the file and check the object is declared at the right place. 130 b, err := ioutil.ReadFile(posn.Filename) 131 if err != nil { 132 t.Errorf("can't read %s: %s", posn.Filename, err) 133 continue 134 } 135 line := string(bytes.Split(b, []byte("\n"))[posn.Line-1]) 136 // Don't assume posn.Column is accurate. 137 if !strings.Contains(line, test.declKeyword+" "+test.name) { 138 t.Errorf("%s: %s not declared here (looking at %q)", posn, obj, line) 139 } 140 } 141} 142