Source file
src/cmd/api/api_test.go
1
2
3
4
5 package main
6
7 import (
8 "flag"
9 "fmt"
10 "go/build"
11 "internal/testenv"
12 "os"
13 "path/filepath"
14 "slices"
15 "strings"
16 "sync"
17 "testing"
18 )
19
20 var flagCheck = flag.Bool("check", false, "run API checks")
21
22 func TestMain(m *testing.M) {
23 flag.Parse()
24 for _, c := range contexts {
25 c.Compiler = build.Default.Compiler
26 }
27 build.Default.GOROOT = testenv.GOROOT(nil)
28
29 os.Exit(m.Run())
30 }
31
32 var (
33 updateGolden = flag.Bool("updategolden", false, "update golden files")
34 )
35
36 func TestGolden(t *testing.T) {
37 if *flagCheck {
38
39 t.Skip("skipping with -check set")
40 }
41
42 testenv.MustHaveGoBuild(t)
43
44 td, err := os.Open("testdata/src/pkg")
45 if err != nil {
46 t.Fatal(err)
47 }
48 fis, err := td.Readdir(0)
49 if err != nil {
50 t.Fatal(err)
51 }
52 for _, fi := range fis {
53 if !fi.IsDir() {
54 continue
55 }
56
57
58 goldenFile := filepath.Join("testdata", "src", "pkg", fi.Name(), "golden.txt")
59 w := NewWalker(nil, "testdata/src/pkg")
60 pkg, err := w.import_(fi.Name())
61 if err != nil {
62 t.Fatalf("import %s: %v", fi.Name(), err)
63 }
64 w.export(pkg)
65
66 if *updateGolden {
67 os.Remove(goldenFile)
68 f, err := os.Create(goldenFile)
69 if err != nil {
70 t.Fatal(err)
71 }
72 for _, feat := range w.Features() {
73 fmt.Fprintf(f, "%s\n", feat)
74 }
75 f.Close()
76 }
77
78 bs, err := os.ReadFile(goldenFile)
79 if err != nil {
80 t.Fatalf("opening golden.txt for package %q: %v", fi.Name(), err)
81 }
82 wanted := strings.Split(string(bs), "\n")
83 slices.Sort(wanted)
84 for _, feature := range wanted {
85 if feature == "" {
86 continue
87 }
88 _, ok := w.features[feature]
89 if !ok {
90 t.Errorf("package %s: missing feature %q", fi.Name(), feature)
91 }
92 delete(w.features, feature)
93 }
94
95 for _, feature := range w.Features() {
96 t.Errorf("package %s: extra feature not in golden file: %q", fi.Name(), feature)
97 }
98 }
99 }
100
101 func TestCompareAPI(t *testing.T) {
102 if *flagCheck {
103
104 t.Skip("skipping with -check set")
105 }
106
107 tests := []struct {
108 name string
109 features, required, exception []string
110 ok bool
111 out string
112 }{
113 {
114 name: "equal",
115 features: []string{"A", "B", "C"},
116 required: []string{"A", "B", "C"},
117 ok: true,
118 out: "",
119 },
120 {
121 name: "feature added",
122 features: []string{"A", "B", "C", "D", "E", "F"},
123 required: []string{"B", "D"},
124 ok: false,
125 out: "+A\n+C\n+E\n+F\n",
126 },
127 {
128 name: "feature removed",
129 features: []string{"C", "A"},
130 required: []string{"A", "B", "C"},
131 ok: false,
132 out: "-B\n",
133 },
134 {
135 name: "exception removal",
136 features: []string{"A", "C"},
137 required: []string{"A", "B", "C"},
138 exception: []string{"B"},
139 ok: true,
140 out: "",
141 },
142
143
144
145
146
147 {
148 name: "contexts reconverging after api/next/* update",
149 features: []string{
150 "A",
151 "pkg syscall, type RawSockaddrInet6 struct",
152 },
153 required: []string{
154 "A",
155 "pkg syscall (darwin-amd64), type RawSockaddrInet6 struct",
156 "pkg syscall, type RawSockaddrInet6 struct",
157 },
158 ok: true,
159 out: "",
160 },
161 {
162 name: "contexts reconverging before api/next/* update",
163 features: []string{
164 "A",
165 "pkg syscall, type RawSockaddrInet6 struct",
166 },
167 required: []string{
168 "A",
169 "pkg syscall (darwin-amd64), type RawSockaddrInet6 struct",
170 },
171 ok: false,
172 out: "+pkg syscall, type RawSockaddrInet6 struct\n",
173 },
174 }
175 for _, tt := range tests {
176 buf := new(strings.Builder)
177 gotOK := compareAPI(buf, tt.features, tt.required, tt.exception)
178 if gotOK != tt.ok {
179 t.Errorf("%s: ok = %v; want %v", tt.name, gotOK, tt.ok)
180 }
181 if got := buf.String(); got != tt.out {
182 t.Errorf("%s: output differs\nGOT:\n%s\nWANT:\n%s", tt.name, got, tt.out)
183 }
184 }
185 }
186
187 func TestSkipInternal(t *testing.T) {
188 if *flagCheck {
189
190 t.Skip("skipping with -check set")
191 }
192
193 tests := []struct {
194 pkg string
195 want bool
196 }{
197 {"net/http", true},
198 {"net/http/internal-foo", true},
199 {"net/http/internal", false},
200 {"net/http/internal/bar", false},
201 {"internal/foo", false},
202 {"internal", false},
203 }
204 for _, tt := range tests {
205 got := !internalPkg.MatchString(tt.pkg)
206 if got != tt.want {
207 t.Errorf("%s is internal = %v; want %v", tt.pkg, got, tt.want)
208 }
209 }
210 }
211
212 func BenchmarkAll(b *testing.B) {
213 for i := 0; i < b.N; i++ {
214 for _, context := range contexts {
215 w := NewWalker(context, filepath.Join(testenv.GOROOT(b), "src"))
216 for _, name := range w.stdPackages {
217 pkg, err := w.import_(name)
218 if _, nogo := err.(*build.NoGoError); nogo {
219 continue
220 }
221 if err != nil {
222 b.Fatalf("import %s (%s-%s): %v", name, context.GOOS, context.GOARCH, err)
223 }
224 w.export(pkg)
225 }
226 w.Features()
227 }
228 }
229 }
230
231 var warmupCache = sync.OnceFunc(func() {
232
233 var wg sync.WaitGroup
234 for _, context := range contexts {
235 wg.Add(1)
236 go func() {
237 defer wg.Done()
238 _ = NewWalker(context, filepath.Join(testenv.GOROOT(nil), "src"))
239 }()
240 }
241 wg.Wait()
242 })
243
244 func TestIssue21181(t *testing.T) {
245 if testing.Short() {
246 t.Skip("skipping with -short")
247 }
248 if *flagCheck {
249
250 t.Skip("skipping with -check set")
251 }
252 testenv.MustHaveGoBuild(t)
253
254 warmupCache()
255
256 for _, context := range contexts {
257 w := NewWalker(context, "testdata/src/issue21181")
258 pkg, err := w.import_("p")
259 if err != nil {
260 t.Fatalf("import %s (%s-%s): %v", "p", context.GOOS, context.GOARCH, err)
261 }
262 w.export(pkg)
263 }
264 }
265
266 func TestIssue29837(t *testing.T) {
267 if testing.Short() {
268 t.Skip("skipping with -short")
269 }
270 if *flagCheck {
271
272 t.Skip("skipping with -check set")
273 }
274 testenv.MustHaveGoBuild(t)
275
276 warmupCache()
277
278 for _, context := range contexts {
279 w := NewWalker(context, "testdata/src/issue29837")
280 _, err := w.ImportFrom("p", "", 0)
281 if _, nogo := err.(*build.NoGoError); !nogo {
282 t.Errorf("expected *build.NoGoError, got %T", err)
283 }
284 }
285 }
286
287 func TestIssue41358(t *testing.T) {
288 if *flagCheck {
289
290 t.Skip("skipping with -check set")
291 }
292 testenv.MustHaveGoBuild(t)
293 context := new(build.Context)
294 *context = build.Default
295 context.Dir = filepath.Join(testenv.GOROOT(t), "src")
296
297 w := NewWalker(context, context.Dir)
298 for _, pkg := range w.stdPackages {
299 if strings.HasPrefix(pkg, "vendor/") || strings.HasPrefix(pkg, "golang.org/x/") {
300 t.Fatalf("stdPackages contains unexpected package %s", pkg)
301 }
302 }
303 }
304
305 func TestIssue64958(t *testing.T) {
306 if testing.Short() {
307 t.Skip("skipping with -short")
308 }
309 if *flagCheck {
310
311 t.Skip("skipping with -check set")
312 }
313 testenv.MustHaveGoBuild(t)
314
315 defer func() {
316 if x := recover(); x != nil {
317 t.Errorf("expected no panic; recovered %v", x)
318 }
319 }()
320 for _, context := range contexts {
321 w := NewWalker(context, "testdata/src/issue64958")
322 pkg, err := w.importFrom("p", "", 0)
323 if err != nil {
324 t.Errorf("expected no error importing; got %T", err)
325 }
326 w.export(pkg)
327 }
328 }
329
330 func TestCheck(t *testing.T) {
331 if !*flagCheck {
332 t.Skip("-check not specified")
333 }
334 testenv.MustHaveGoBuild(t)
335 Check(t)
336 }
337
View as plain text