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 context := context
236 wg.Add(1)
237 go func() {
238 defer wg.Done()
239 _ = NewWalker(context, filepath.Join(testenv.GOROOT(nil), "src"))
240 }()
241 }
242 wg.Wait()
243 })
244
245 func TestIssue21181(t *testing.T) {
246 if testing.Short() {
247 t.Skip("skipping with -short")
248 }
249 if *flagCheck {
250
251 t.Skip("skipping with -check set")
252 }
253 testenv.MustHaveGoBuild(t)
254
255 warmupCache()
256
257 for _, context := range contexts {
258 w := NewWalker(context, "testdata/src/issue21181")
259 pkg, err := w.import_("p")
260 if err != nil {
261 t.Fatalf("import %s (%s-%s): %v", "p", context.GOOS, context.GOARCH, err)
262 }
263 w.export(pkg)
264 }
265 }
266
267 func TestIssue29837(t *testing.T) {
268 if testing.Short() {
269 t.Skip("skipping with -short")
270 }
271 if *flagCheck {
272
273 t.Skip("skipping with -check set")
274 }
275 testenv.MustHaveGoBuild(t)
276
277 warmupCache()
278
279 for _, context := range contexts {
280 w := NewWalker(context, "testdata/src/issue29837")
281 _, err := w.ImportFrom("p", "", 0)
282 if _, nogo := err.(*build.NoGoError); !nogo {
283 t.Errorf("expected *build.NoGoError, got %T", err)
284 }
285 }
286 }
287
288 func TestIssue41358(t *testing.T) {
289 if *flagCheck {
290
291 t.Skip("skipping with -check set")
292 }
293 testenv.MustHaveGoBuild(t)
294 context := new(build.Context)
295 *context = build.Default
296 context.Dir = filepath.Join(testenv.GOROOT(t), "src")
297
298 w := NewWalker(context, context.Dir)
299 for _, pkg := range w.stdPackages {
300 if strings.HasPrefix(pkg, "vendor/") || strings.HasPrefix(pkg, "golang.org/x/") {
301 t.Fatalf("stdPackages contains unexpected package %s", pkg)
302 }
303 }
304 }
305
306 func TestIssue64958(t *testing.T) {
307 if testing.Short() {
308 t.Skip("skipping with -short")
309 }
310 if *flagCheck {
311
312 t.Skip("skipping with -check set")
313 }
314 testenv.MustHaveGoBuild(t)
315
316 defer func() {
317 if x := recover(); x != nil {
318 t.Errorf("expected no panic; recovered %v", x)
319 }
320 }()
321 for _, context := range contexts {
322 w := NewWalker(context, "testdata/src/issue64958")
323 pkg, err := w.importFrom("p", "", 0)
324 if err != nil {
325 t.Errorf("expected no error importing; got %T", err)
326 }
327 w.export(pkg)
328 }
329 }
330
331 func TestCheck(t *testing.T) {
332 if !*flagCheck {
333 t.Skip("-check not specified")
334 }
335 testenv.MustHaveGoBuild(t)
336 Check(t)
337 }
338
View as plain text