1
2
3
4
5 package gcimporter
6
7 import (
8 "bytes"
9 "fmt"
10 "internal/testenv"
11 "os"
12 "os/exec"
13 "path/filepath"
14 "runtime"
15 "strings"
16 "testing"
17 "time"
18
19 "go/token"
20 "go/types"
21 )
22
23
24
25
26 func skipSpecialPlatforms(t *testing.T) {
27 switch platform := runtime.GOOS + "-" + runtime.GOARCH; platform {
28 case "darwin-arm64":
29 t.Skipf("no compiled packages available for import on %s", platform)
30 }
31 }
32
33
34
35 func compile(t *testing.T, dirname, filename, outdirname string) string {
36
37 if !strings.HasSuffix(filename, ".go") {
38 t.Fatalf("filename doesn't end in .go: %s", filename)
39 }
40 basename := filepath.Base(filename)
41 outname := filepath.Join(outdirname, basename[:len(basename)-2]+"o")
42 cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "-o", outname, filename)
43 cmd.Dir = dirname
44 out, err := cmd.CombinedOutput()
45 if err != nil {
46 t.Logf("%s", out)
47 t.Fatalf("go tool compile %s failed: %s", filename, err)
48 }
49 return outname
50 }
51
52 func testPath(t *testing.T, path, srcDir string) *types.Package {
53 t0 := time.Now()
54 fset := token.NewFileSet()
55 pkg, err := Import(fset, make(map[string]*types.Package), path, srcDir, nil)
56 if err != nil {
57 t.Errorf("testPath(%s): %s", path, err)
58 return nil
59 }
60 t.Logf("testPath(%s): %v", path, time.Since(t0))
61 return pkg
62 }
63
64 const maxTime = 30 * time.Second
65
66 func testDir(t *testing.T, dir string, endTime time.Time) (nimports int) {
67 dirname := filepath.Join(runtime.GOROOT(), "pkg", runtime.GOOS+"_"+runtime.GOARCH, dir)
68 list, err := os.ReadDir(dirname)
69 if err != nil {
70 t.Fatalf("testDir(%s): %s", dirname, err)
71 }
72 for _, f := range list {
73 if time.Now().After(endTime) {
74 t.Log("testing time used up")
75 return
76 }
77 switch {
78 case !f.IsDir():
79
80 for _, ext := range pkgExts {
81 if strings.HasSuffix(f.Name(), ext) {
82 name := f.Name()[0 : len(f.Name())-len(ext)]
83 if testPath(t, filepath.Join(dir, name), dir) != nil {
84 nimports++
85 }
86 }
87 }
88 case f.IsDir():
89 nimports += testDir(t, filepath.Join(dir, f.Name()), endTime)
90 }
91 }
92 return
93 }
94
95 func mktmpdir(t *testing.T) string {
96 tmpdir, err := os.MkdirTemp("", "gcimporter_test")
97 if err != nil {
98 t.Fatal("mktmpdir:", err)
99 }
100 if err := os.Mkdir(filepath.Join(tmpdir, "testdata"), 0700); err != nil {
101 os.RemoveAll(tmpdir)
102 t.Fatal("mktmpdir:", err)
103 }
104 return tmpdir
105 }
106
107 func TestImportTestdata(t *testing.T) {
108
109 if runtime.Compiler != "gc" {
110 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
111 }
112
113 tmpdir := mktmpdir(t)
114 defer os.RemoveAll(tmpdir)
115
116 compile(t, "testdata", "exports.go", filepath.Join(tmpdir, "testdata"))
117
118 if pkg := testPath(t, "./testdata/exports", tmpdir); pkg != nil {
119
120
121
122
123
124
125
126
127
128 got := fmt.Sprint(pkg.Imports())
129 for _, want := range []string{"go/ast", "go/token"} {
130 if !strings.Contains(got, want) {
131 t.Errorf(`Package("exports").Imports() = %s, does not contain %s`, got, want)
132 }
133 }
134 }
135 }
136
137 func TestVersionHandling(t *testing.T) {
138 skipSpecialPlatforms(t)
139
140
141 if runtime.Compiler != "gc" {
142 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
143 }
144
145 const dir = "./testdata/versions"
146 list, err := os.ReadDir(dir)
147 if err != nil {
148 t.Fatal(err)
149 }
150
151 tmpdir := mktmpdir(t)
152 defer os.RemoveAll(tmpdir)
153 corruptdir := filepath.Join(tmpdir, "testdata", "versions")
154 if err := os.Mkdir(corruptdir, 0700); err != nil {
155 t.Fatal(err)
156 }
157
158 fset := token.NewFileSet()
159
160 for _, f := range list {
161 name := f.Name()
162 if !strings.HasSuffix(name, ".a") {
163 continue
164 }
165 if strings.Contains(name, "corrupted") {
166 continue
167 }
168 pkgpath := "./" + name[:len(name)-2]
169
170 if testing.Verbose() {
171 t.Logf("importing %s", name)
172 }
173
174
175 _, err := Import(fset, make(map[string]*types.Package), pkgpath, dir, nil)
176 if err != nil {
177
178 if strings.Contains(err.Error(), "no longer supported") {
179 switch name {
180 case "test_go1.7_0.a", "test_go1.7_1.a",
181 "test_go1.8_4.a", "test_go1.8_5.a",
182 "test_go1.11_6b.a", "test_go1.11_999b.a":
183 continue
184 }
185
186 }
187
188 if strings.Contains(err.Error(), "newer version") {
189 switch name {
190 case "test_go1.11_999i.a":
191 continue
192 }
193
194 }
195 t.Errorf("import %q failed: %v", pkgpath, err)
196 continue
197 }
198
199
200
201 data, err := os.ReadFile(filepath.Join(dir, name))
202 if err != nil {
203 t.Fatal(err)
204 }
205
206 i := bytes.Index(data, []byte("\n$$B\n")) + 5
207 j := bytes.Index(data[i:], []byte("\n$$\n")) + i
208 if i < 0 || j < 0 || i > j {
209 t.Fatalf("export data section not found (i = %d, j = %d)", i, j)
210 }
211
212 for k := j - 13; k >= i; k -= 7 {
213 data[k]++
214 }
215
216 pkgpath += "_corrupted"
217 filename := filepath.Join(corruptdir, pkgpath) + ".a"
218 os.WriteFile(filename, data, 0666)
219
220
221 _, err = Import(fset, make(map[string]*types.Package), pkgpath, corruptdir, nil)
222 if err == nil {
223 t.Errorf("import corrupted %q succeeded", pkgpath)
224 } else if msg := err.Error(); !strings.Contains(msg, "version skew") {
225 t.Errorf("import %q error incorrect (%s)", pkgpath, msg)
226 }
227 }
228 }
229
230 func TestImportStdLib(t *testing.T) {
231 skipSpecialPlatforms(t)
232
233
234 if runtime.Compiler != "gc" {
235 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
236 }
237
238 dt := maxTime
239 if testing.Short() && testenv.Builder() == "" {
240 dt = 10 * time.Millisecond
241 }
242 nimports := testDir(t, "", time.Now().Add(dt))
243 t.Logf("tested %d imports", nimports)
244 }
245
246 var importedObjectTests = []struct {
247 name string
248 want string
249 }{
250
251 {"crypto.Hash", "type Hash uint"},
252 {"go/ast.ObjKind", "type ObjKind int"},
253 {"go/types.Qualifier", "type Qualifier func(*Package) string"},
254 {"go/types.Comparable", "func Comparable(T Type) bool"},
255 {"math.Pi", "const Pi untyped float"},
256 {"math.Sin", "func Sin(x float64) float64"},
257 {"go/ast.NotNilFilter", "func NotNilFilter(_ string, v reflect.Value) bool"},
258 {"go/internal/gcimporter.FindPkg", "func FindPkg(path string, srcDir string) (filename string, id string)"},
259
260
261 {"context.Context", "type Context interface{Deadline() (deadline time.Time, ok bool); Done() <-chan struct{}; Err() error; Value(key interface{}) interface{}}"},
262 {"crypto.Decrypter", "type Decrypter interface{Decrypt(rand io.Reader, msg []byte, opts DecrypterOpts) (plaintext []byte, err error); Public() PublicKey}"},
263 {"encoding.BinaryMarshaler", "type BinaryMarshaler interface{MarshalBinary() (data []byte, err error)}"},
264 {"io.Reader", "type Reader interface{Read(p []byte) (n int, err error)}"},
265 {"io.ReadWriter", "type ReadWriter interface{Reader; Writer}"},
266 {"go/ast.Node", "type Node interface{End() go/token.Pos; Pos() go/token.Pos}"},
267 {"go/types.Type", "type Type interface{String() string; Underlying() Type}"},
268 }
269
270 func TestImportedTypes(t *testing.T) {
271 skipSpecialPlatforms(t)
272
273
274 if runtime.Compiler != "gc" {
275 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
276 }
277
278 fset := token.NewFileSet()
279 for _, test := range importedObjectTests {
280 s := strings.Split(test.name, ".")
281 if len(s) != 2 {
282 t.Fatal("inconsistent test data")
283 }
284 importPath := s[0]
285 objName := s[1]
286
287 pkg, err := Import(fset, make(map[string]*types.Package), importPath, ".", nil)
288 if err != nil {
289 t.Error(err)
290 continue
291 }
292
293 obj := pkg.Scope().Lookup(objName)
294 if obj == nil {
295 t.Errorf("%s: object not found", test.name)
296 continue
297 }
298
299 got := types.ObjectString(obj, types.RelativeTo(pkg))
300 if got != test.want {
301 t.Errorf("%s: got %q; want %q", test.name, got, test.want)
302 }
303
304 if named, _ := obj.Type().(*types.Named); named != nil {
305 verifyInterfaceMethodRecvs(t, named, 0)
306 }
307 }
308 }
309
310
311
312 func verifyInterfaceMethodRecvs(t *testing.T, named *types.Named, level int) {
313
314 if level > 10 {
315 t.Errorf("%s: embeds itself", named)
316 return
317 }
318
319 iface, _ := named.Underlying().(*types.Interface)
320 if iface == nil {
321 return
322 }
323
324
325 for i := 0; i < iface.NumExplicitMethods(); i++ {
326 m := iface.ExplicitMethod(i)
327 recv := m.Type().(*types.Signature).Recv()
328 if recv == nil {
329 t.Errorf("%s: missing receiver type", m)
330 continue
331 }
332 if recv.Type() != named {
333 t.Errorf("%s: got recv type %s; want %s", m, recv.Type(), named)
334 }
335 }
336
337
338 for i := 0; i < iface.NumEmbeddeds(); i++ {
339
340 if etype, _ := iface.EmbeddedType(i).(*types.Named); etype != nil {
341 verifyInterfaceMethodRecvs(t, etype, level+1)
342 }
343 }
344 }
345
346 func TestIssue5815(t *testing.T) {
347 skipSpecialPlatforms(t)
348
349
350 if runtime.Compiler != "gc" {
351 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
352 }
353
354 pkg := importPkg(t, "strings", ".")
355
356 scope := pkg.Scope()
357 for _, name := range scope.Names() {
358 obj := scope.Lookup(name)
359 if obj.Pkg() == nil {
360 t.Errorf("no pkg for %s", obj)
361 }
362 if tname, _ := obj.(*types.TypeName); tname != nil {
363 named := tname.Type().(*types.Named)
364 for i := 0; i < named.NumMethods(); i++ {
365 m := named.Method(i)
366 if m.Pkg() == nil {
367 t.Errorf("no pkg for %s", m)
368 }
369 }
370 }
371 }
372 }
373
374
375 func TestCorrectMethodPackage(t *testing.T) {
376 skipSpecialPlatforms(t)
377
378
379 if runtime.Compiler != "gc" {
380 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
381 }
382
383 imports := make(map[string]*types.Package)
384 fset := token.NewFileSet()
385 _, err := Import(fset, imports, "net/http", ".", nil)
386 if err != nil {
387 t.Fatal(err)
388 }
389
390 mutex := imports["sync"].Scope().Lookup("Mutex").(*types.TypeName).Type()
391 mset := types.NewMethodSet(types.NewPointer(mutex))
392 sel := mset.Lookup(nil, "Lock")
393 lock := sel.Obj().(*types.Func)
394 if got, want := lock.Pkg().Path(), "sync"; got != want {
395 t.Errorf("got package path %q; want %q", got, want)
396 }
397 }
398
399 func TestIssue13566(t *testing.T) {
400 skipSpecialPlatforms(t)
401
402
403 if runtime.Compiler != "gc" {
404 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
405 }
406
407
408
409 if runtime.GOOS == "windows" {
410 t.Skip("avoid dealing with relative paths/drive letters on windows")
411 }
412
413 tmpdir := mktmpdir(t)
414 defer os.RemoveAll(tmpdir)
415 testoutdir := filepath.Join(tmpdir, "testdata")
416
417
418
419
420 bpath, err := filepath.Abs(filepath.Join("testdata", "b.go"))
421 if err != nil {
422 t.Fatal(err)
423 }
424 compile(t, "testdata", "a.go", testoutdir)
425 compile(t, testoutdir, bpath, testoutdir)
426
427
428 pkg := importPkg(t, "./testdata/b", tmpdir)
429
430
431 for _, imp := range pkg.Imports() {
432 if imp.Name() == "" {
433 t.Errorf("no name for %s package", imp.Path())
434 }
435 }
436 }
437
438 func TestIssue13898(t *testing.T) {
439 skipSpecialPlatforms(t)
440
441
442 if runtime.Compiler != "gc" {
443 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
444 }
445
446
447 fset := token.NewFileSet()
448 imports := make(map[string]*types.Package)
449 _, err := Import(fset, imports, "go/internal/gcimporter", ".", nil)
450 if err != nil {
451 t.Fatal(err)
452 }
453
454
455 var goTypesPkg *types.Package
456 for path, pkg := range imports {
457 if path == "go/types" {
458 goTypesPkg = pkg
459 break
460 }
461 }
462 if goTypesPkg == nil {
463 t.Fatal("go/types not found")
464 }
465
466
467 obj := lookupObj(t, goTypesPkg.Scope(), "Object")
468 typ, ok := obj.Type().(*types.Named)
469 if !ok {
470 t.Fatalf("go/types.Object type is %v; wanted named type", typ)
471 }
472
473
474 m, index, indirect := types.LookupFieldOrMethod(typ, false, nil, "Pkg")
475 if m == nil {
476 t.Fatalf("go/types.Object.Pkg not found (index = %v, indirect = %v)", index, indirect)
477 }
478
479
480 if m.Pkg().Path() != "go/types" {
481 t.Fatalf("found %v; want go/types", m.Pkg())
482 }
483 }
484
485 func TestIssue15517(t *testing.T) {
486 skipSpecialPlatforms(t)
487
488
489 if runtime.Compiler != "gc" {
490 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
491 }
492
493
494
495 if runtime.GOOS == "windows" {
496 t.Skip("avoid dealing with relative paths/drive letters on windows")
497 }
498
499 tmpdir := mktmpdir(t)
500 defer os.RemoveAll(tmpdir)
501
502 compile(t, "testdata", "p.go", filepath.Join(tmpdir, "testdata"))
503
504
505
506
507
508
509
510
511
512
513
514
515
516 imports := make(map[string]*types.Package)
517 fset := token.NewFileSet()
518 for i := 0; i < 3; i++ {
519 if _, err := Import(fset, imports, "./././testdata/p", tmpdir, nil); err != nil {
520 t.Fatal(err)
521 }
522 }
523 }
524
525 func TestIssue15920(t *testing.T) {
526 skipSpecialPlatforms(t)
527
528
529 if runtime.Compiler != "gc" {
530 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
531 }
532
533
534
535 if runtime.GOOS == "windows" {
536 t.Skip("avoid dealing with relative paths/drive letters on windows")
537 }
538
539 compileAndImportPkg(t, "issue15920")
540 }
541
542 func TestIssue20046(t *testing.T) {
543 skipSpecialPlatforms(t)
544
545
546 if runtime.Compiler != "gc" {
547 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
548 }
549
550
551
552 if runtime.GOOS == "windows" {
553 t.Skip("avoid dealing with relative paths/drive letters on windows")
554 }
555
556
557 pkg := compileAndImportPkg(t, "issue20046")
558 obj := lookupObj(t, pkg.Scope(), "V")
559 if m, index, indirect := types.LookupFieldOrMethod(obj.Type(), false, nil, "M"); m == nil {
560 t.Fatalf("V.M not found (index = %v, indirect = %v)", index, indirect)
561 }
562 }
563 func TestIssue25301(t *testing.T) {
564 skipSpecialPlatforms(t)
565
566
567 if runtime.Compiler != "gc" {
568 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
569 }
570
571
572
573 if runtime.GOOS == "windows" {
574 t.Skip("avoid dealing with relative paths/drive letters on windows")
575 }
576
577 compileAndImportPkg(t, "issue25301")
578 }
579
580 func TestIssue25596(t *testing.T) {
581 skipSpecialPlatforms(t)
582
583
584 if runtime.Compiler != "gc" {
585 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
586 }
587
588
589
590 if runtime.GOOS == "windows" {
591 t.Skip("avoid dealing with relative paths/drive letters on windows")
592 }
593
594 compileAndImportPkg(t, "issue25596")
595 }
596
597 func importPkg(t *testing.T, path, srcDir string) *types.Package {
598 fset := token.NewFileSet()
599 pkg, err := Import(fset, make(map[string]*types.Package), path, srcDir, nil)
600 if err != nil {
601 t.Fatal(err)
602 }
603 return pkg
604 }
605
606 func compileAndImportPkg(t *testing.T, name string) *types.Package {
607 tmpdir := mktmpdir(t)
608 defer os.RemoveAll(tmpdir)
609 compile(t, "testdata", name+".go", filepath.Join(tmpdir, "testdata"))
610 return importPkg(t, "./testdata/"+name, tmpdir)
611 }
612
613 func lookupObj(t *testing.T, scope *types.Scope, name string) types.Object {
614 if obj := scope.Lookup(name); obj != nil {
615 return obj
616 }
617 t.Fatalf("%s not found", name)
618 return nil
619 }
620
View as plain text