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