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 tests := []struct {
103 name string
104 features, required, exception []string
105 ok bool
106 out string
107 }{
108 {
109 name: "equal",
110 features: []string{"A", "B", "C"},
111 required: []string{"A", "B", "C"},
112 ok: true,
113 out: "",
114 },
115 {
116 name: "feature added",
117 features: []string{"A", "B", "C", "D", "E", "F"},
118 required: []string{"B", "D"},
119 ok: false,
120 out: "+A\n+C\n+E\n+F\n",
121 },
122 {
123 name: "feature removed",
124 features: []string{"C", "A"},
125 required: []string{"A", "B", "C"},
126 ok: false,
127 out: "-B\n",
128 },
129 {
130 name: "exception removal",
131 features: []string{"A", "C"},
132 required: []string{"A", "B", "C"},
133 exception: []string{"B"},
134 ok: true,
135 out: "",
136 },
137
138
139
140
141
142 {
143 name: "contexts reconverging after api/next/* update",
144 features: []string{
145 "A",
146 "pkg syscall, type RawSockaddrInet6 struct",
147 },
148 required: []string{
149 "A",
150 "pkg syscall (darwin-amd64), type RawSockaddrInet6 struct",
151 "pkg syscall, type RawSockaddrInet6 struct",
152 },
153 ok: true,
154 out: "",
155 },
156 {
157 name: "contexts reconverging before api/next/* update",
158 features: []string{
159 "A",
160 "pkg syscall, type RawSockaddrInet6 struct",
161 },
162 required: []string{
163 "A",
164 "pkg syscall (darwin-amd64), type RawSockaddrInet6 struct",
165 },
166 ok: false,
167 out: "+pkg syscall, type RawSockaddrInet6 struct\n",
168 },
169 }
170 for _, tt := range tests {
171 buf := new(strings.Builder)
172 gotOK := compareAPI(buf, tt.features, tt.required, tt.exception)
173 if gotOK != tt.ok {
174 t.Errorf("%s: ok = %v; want %v", tt.name, gotOK, tt.ok)
175 }
176 if got := buf.String(); got != tt.out {
177 t.Errorf("%s: output differs\nGOT:\n%s\nWANT:\n%s", tt.name, got, tt.out)
178 }
179 }
180 }
181
182 func TestSkipInternal(t *testing.T) {
183 tests := []struct {
184 pkg string
185 want bool
186 }{
187 {"net/http", true},
188 {"net/http/internal-foo", true},
189 {"net/http/internal", false},
190 {"net/http/internal/bar", false},
191 {"internal/foo", false},
192 {"internal", false},
193 }
194 for _, tt := range tests {
195 got := !internalPkg.MatchString(tt.pkg)
196 if got != tt.want {
197 t.Errorf("%s is internal = %v; want %v", tt.pkg, got, tt.want)
198 }
199 }
200 }
201
202 func BenchmarkAll(b *testing.B) {
203 for i := 0; i < b.N; i++ {
204 for _, context := range contexts {
205 w := NewWalker(context, filepath.Join(testenv.GOROOT(b), "src"))
206 for _, name := range w.stdPackages {
207 pkg, err := w.import_(name)
208 if _, nogo := err.(*build.NoGoError); nogo {
209 continue
210 }
211 if err != nil {
212 b.Fatalf("import %s (%s-%s): %v", name, context.GOOS, context.GOARCH, err)
213 }
214 w.export(pkg)
215 }
216 w.Features()
217 }
218 }
219 }
220
221 var warmupCache = sync.OnceFunc(func() {
222
223 var wg sync.WaitGroup
224 for _, context := range contexts {
225 context := context
226 wg.Add(1)
227 go func() {
228 defer wg.Done()
229 _ = NewWalker(context, filepath.Join(testenv.GOROOT(nil), "src"))
230 }()
231 }
232 wg.Wait()
233 })
234
235 func TestIssue21181(t *testing.T) {
236 if testing.Short() {
237 t.Skip("skipping with -short")
238 }
239 if *flagCheck {
240
241 t.Skip("skipping with -check set")
242 }
243 testenv.MustHaveGoBuild(t)
244
245 warmupCache()
246
247 for _, context := range contexts {
248 w := NewWalker(context, "testdata/src/issue21181")
249 pkg, err := w.import_("p")
250 if err != nil {
251 t.Fatalf("import %s (%s-%s): %v", "p", context.GOOS, context.GOARCH, err)
252 }
253 w.export(pkg)
254 }
255 }
256
257 func TestIssue29837(t *testing.T) {
258 if testing.Short() {
259 t.Skip("skipping with -short")
260 }
261 if *flagCheck {
262
263 t.Skip("skipping with -check set")
264 }
265 testenv.MustHaveGoBuild(t)
266
267 warmupCache()
268
269 for _, context := range contexts {
270 w := NewWalker(context, "testdata/src/issue29837")
271 _, err := w.ImportFrom("p", "", 0)
272 if _, nogo := err.(*build.NoGoError); !nogo {
273 t.Errorf("expected *build.NoGoError, got %T", err)
274 }
275 }
276 }
277
278 func TestIssue41358(t *testing.T) {
279 if *flagCheck {
280
281 t.Skip("skipping with -check set")
282 }
283 testenv.MustHaveGoBuild(t)
284 context := new(build.Context)
285 *context = build.Default
286 context.Dir = filepath.Join(testenv.GOROOT(t), "src")
287
288 w := NewWalker(context, context.Dir)
289 for _, pkg := range w.stdPackages {
290 if strings.HasPrefix(pkg, "vendor/") || strings.HasPrefix(pkg, "golang.org/x/") {
291 t.Fatalf("stdPackages contains unexpected package %s", pkg)
292 }
293 }
294 }
295
296 func TestIssue64958(t *testing.T) {
297 defer func() {
298 if x := recover(); x != nil {
299 t.Errorf("expected no panic; recovered %v", x)
300 }
301 }()
302
303 testenv.MustHaveGoBuild(t)
304
305 for _, context := range contexts {
306 w := NewWalker(context, "testdata/src/issue64958")
307 pkg, err := w.importFrom("p", "", 0)
308 if err != nil {
309 t.Errorf("expected no error importing; got %T", err)
310 }
311 w.export(pkg)
312 }
313 }
314
315 func TestCheck(t *testing.T) {
316 if !*flagCheck {
317 t.Skip("-check not specified")
318 }
319 testenv.MustHaveGoBuild(t)
320 Check(t)
321 }
322
View as plain text