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