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 loader_test |
6 | |
7 | // This file enumerates all packages beneath $GOROOT, loads them, plus |
8 | // their external tests if any, runs the type checker on them, and |
9 | // prints some summary information. |
10 | |
11 | import ( |
12 | "bytes" |
13 | "fmt" |
14 | "go/ast" |
15 | "go/build" |
16 | "go/token" |
17 | "go/types" |
18 | "io/ioutil" |
19 | "path/filepath" |
20 | "runtime" |
21 | "strings" |
22 | "testing" |
23 | "time" |
24 | |
25 | "golang.org/x/tools/go/buildutil" |
26 | "golang.org/x/tools/go/loader" |
27 | "golang.org/x/tools/internal/testenv" |
28 | ) |
29 | |
30 | func TestStdlib(t *testing.T) { |
31 | if runtime.GOOS == "android" { |
32 | t.Skipf("incomplete std lib on %s", runtime.GOOS) |
33 | } |
34 | if testing.Short() { |
35 | t.Skip("skipping in short mode; uses tons of memory (https://golang.org/issue/14113)") |
36 | } |
37 | testenv.NeedsTool(t, "go") |
38 | |
39 | runtime.GC() |
40 | t0 := time.Now() |
41 | var memstats runtime.MemStats |
42 | runtime.ReadMemStats(&memstats) |
43 | alloc := memstats.Alloc |
44 | |
45 | // Load, parse and type-check the program. |
46 | ctxt := build.Default // copy |
47 | ctxt.GOPATH = "" // disable GOPATH |
48 | conf := loader.Config{Build: &ctxt} |
49 | for _, path := range buildutil.AllPackages(conf.Build) { |
50 | conf.ImportWithTests(path) |
51 | } |
52 | |
53 | prog, err := conf.Load() |
54 | if err != nil { |
55 | t.Fatalf("Load failed: %v", err) |
56 | } |
57 | |
58 | t1 := time.Now() |
59 | runtime.GC() |
60 | runtime.ReadMemStats(&memstats) |
61 | |
62 | numPkgs := len(prog.AllPackages) |
63 | if want := 205; numPkgs < want { |
64 | t.Errorf("Loaded only %d packages, want at least %d", numPkgs, want) |
65 | } |
66 | |
67 | // Dump package members. |
68 | if false { |
69 | for pkg := range prog.AllPackages { |
70 | fmt.Printf("Package %s:\n", pkg.Path()) |
71 | scope := pkg.Scope() |
72 | qualifier := types.RelativeTo(pkg) |
73 | for _, name := range scope.Names() { |
74 | if ast.IsExported(name) { |
75 | fmt.Printf("\t%s\n", types.ObjectString(scope.Lookup(name), qualifier)) |
76 | } |
77 | } |
78 | fmt.Println() |
79 | } |
80 | } |
81 | |
82 | // Check that Test functions for regexp and compress/bzip2 are |
83 | // simultaneously present. The apparent cycle formed when augmenting |
84 | // these packages by their tests (together with io/ioutil's test, which is now |
85 | // an xtest) was the original motivation or reporting golang.org/issue/7114. |
86 | // |
87 | // compress/bzip2.TestBitReader in bzip2_test.go imports io/ioutil |
88 | // io/ioutil.TestTempFile in tempfile_test.go imports regexp (no longer exists) |
89 | // regexp.TestRE2Search in exec_test.go imports compress/bzip2 |
90 | for _, test := range []struct{ pkg, fn string }{ |
91 | {"regexp", "TestRE2Search"}, |
92 | {"compress/bzip2", "TestBitReader"}, |
93 | } { |
94 | info := prog.Imported[test.pkg] |
95 | if info == nil { |
96 | t.Errorf("failed to load package %q", test.pkg) |
97 | continue |
98 | } |
99 | obj, _ := info.Pkg.Scope().Lookup(test.fn).(*types.Func) |
100 | if obj == nil { |
101 | t.Errorf("package %q has no func %q", test.pkg, test.fn) |
102 | continue |
103 | } |
104 | } |
105 | |
106 | // Dump some statistics. |
107 | |
108 | // determine line count |
109 | var lineCount int |
110 | prog.Fset.Iterate(func(f *token.File) bool { |
111 | lineCount += f.LineCount() |
112 | return true |
113 | }) |
114 | |
115 | t.Log("GOMAXPROCS: ", runtime.GOMAXPROCS(0)) |
116 | t.Log("#Source lines: ", lineCount) |
117 | t.Log("Load/parse/typecheck: ", t1.Sub(t0)) |
118 | t.Log("#MB: ", int64(memstats.Alloc-alloc)/1000000) |
119 | } |
120 | |
121 | func TestCgoOption(t *testing.T) { |
122 | if testing.Short() { |
123 | t.Skip("skipping in short mode; uses tons of memory (https://golang.org/issue/14113)") |
124 | } |
125 | switch runtime.GOOS { |
126 | // On these systems, the net and os/user packages don't use cgo |
127 | // or the std library is incomplete (Android). |
128 | case "android", "plan9", "solaris", "windows": |
129 | t.Skipf("no cgo or incomplete std lib on %s", runtime.GOOS) |
130 | } |
131 | // In nocgo builds (e.g. linux-amd64-nocgo), |
132 | // there is no "runtime/cgo" package, |
133 | // so cgo-generated Go files will have a failing import. |
134 | if !build.Default.CgoEnabled { |
135 | return |
136 | } |
137 | testenv.NeedsTool(t, "go") |
138 | |
139 | // Test that we can load cgo-using packages with |
140 | // CGO_ENABLED=[01], which causes go/build to select pure |
141 | // Go/native implementations, respectively, based on build |
142 | // tags. |
143 | // |
144 | // Each entry specifies a package-level object and the generic |
145 | // file expected to define it when cgo is disabled. |
146 | // When cgo is enabled, the exact file is not specified (since |
147 | // it varies by platform), but must differ from the generic one. |
148 | // |
149 | // The test also loads the actual file to verify that the |
150 | // object is indeed defined at that location. |
151 | for _, test := range []struct { |
152 | pkg, name, genericFile string |
153 | }{ |
154 | {"net", "cgoLookupHost", "cgo_stub.go"}, |
155 | {"os/user", "current", "lookup_stubs.go"}, |
156 | } { |
157 | ctxt := build.Default |
158 | for _, ctxt.CgoEnabled = range []bool{false, true} { |
159 | conf := loader.Config{Build: &ctxt} |
160 | conf.Import(test.pkg) |
161 | prog, err := conf.Load() |
162 | if err != nil { |
163 | t.Errorf("Load failed: %v", err) |
164 | continue |
165 | } |
166 | info := prog.Imported[test.pkg] |
167 | if info == nil { |
168 | t.Errorf("package %s not found", test.pkg) |
169 | continue |
170 | } |
171 | obj := info.Pkg.Scope().Lookup(test.name) |
172 | if obj == nil { |
173 | t.Errorf("no object %s.%s", test.pkg, test.name) |
174 | continue |
175 | } |
176 | posn := prog.Fset.Position(obj.Pos()) |
177 | t.Logf("%s: %s (CgoEnabled=%t)", posn, obj, ctxt.CgoEnabled) |
178 | |
179 | gotFile := filepath.Base(posn.Filename) |
180 | filesMatch := gotFile == test.genericFile |
181 | |
182 | if ctxt.CgoEnabled && filesMatch { |
183 | t.Errorf("CGO_ENABLED=1: %s found in %s, want native file", |
184 | obj, gotFile) |
185 | } else if !ctxt.CgoEnabled && !filesMatch { |
186 | t.Errorf("CGO_ENABLED=0: %s found in %s, want %s", |
187 | obj, gotFile, test.genericFile) |
188 | } |
189 | |
190 | // Load the file and check the object is declared at the right place. |
191 | b, err := ioutil.ReadFile(posn.Filename) |
192 | if err != nil { |
193 | t.Errorf("can't read %s: %s", posn.Filename, err) |
194 | continue |
195 | } |
196 | line := string(bytes.Split(b, []byte("\n"))[posn.Line-1]) |
197 | ident := line[posn.Column-1:] |
198 | if !strings.HasPrefix(ident, test.name) { |
199 | t.Errorf("%s: %s not declared here (looking at %q)", posn, obj, ident) |
200 | } |
201 | } |
202 | } |
203 | } |
204 |
Members