]> Cypherpunks.ru repositories - gostls13.git/blob - src/go/internal/srcimporter/srcimporter_test.go
misc/cgo/test: simplify for module mode
[gostls13.git] / src / go / internal / srcimporter / srcimporter_test.go
1 // Copyright 2017 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package srcimporter
6
7 import (
8         "flag"
9         "go/build"
10         "go/token"
11         "go/types"
12         "internal/testenv"
13         "os"
14         "path"
15         "path/filepath"
16         "strings"
17         "testing"
18         "time"
19 )
20
21 func TestMain(m *testing.M) {
22         flag.Parse()
23         build.Default.GOROOT = testenv.GOROOT(nil)
24         os.Exit(m.Run())
25 }
26
27 const maxTime = 2 * time.Second
28
29 var importer = New(&build.Default, token.NewFileSet(), make(map[string]*types.Package))
30
31 func doImport(t *testing.T, path, srcDir string) {
32         t0 := time.Now()
33         if _, err := importer.ImportFrom(path, srcDir, 0); err != nil {
34                 // don't report an error if there's no buildable Go files
35                 if _, nogo := err.(*build.NoGoError); !nogo {
36                         t.Errorf("import %q failed (%v)", path, err)
37                 }
38                 return
39         }
40         t.Logf("import %q: %v", path, time.Since(t0))
41 }
42
43 // walkDir imports the all the packages with the given path
44 // prefix recursively. It returns the number of packages
45 // imported and whether importing was aborted because time
46 // has passed endTime.
47 func walkDir(t *testing.T, path string, endTime time.Time) (int, bool) {
48         if time.Now().After(endTime) {
49                 t.Log("testing time used up")
50                 return 0, true
51         }
52
53         // ignore fake packages and testdata directories
54         if path == "builtin" || path == "unsafe" || strings.HasSuffix(path, "testdata") {
55                 return 0, false
56         }
57
58         list, err := os.ReadDir(filepath.Join(testenv.GOROOT(t), "src", path))
59         if err != nil {
60                 t.Fatalf("walkDir %s failed (%v)", path, err)
61         }
62
63         nimports := 0
64         hasGoFiles := false
65         for _, f := range list {
66                 if f.IsDir() {
67                         n, abort := walkDir(t, filepath.Join(path, f.Name()), endTime)
68                         nimports += n
69                         if abort {
70                                 return nimports, true
71                         }
72                 } else if strings.HasSuffix(f.Name(), ".go") {
73                         hasGoFiles = true
74                 }
75         }
76
77         if hasGoFiles {
78                 doImport(t, path, "")
79                 nimports++
80         }
81
82         return nimports, false
83 }
84
85 func TestImportStdLib(t *testing.T) {
86         if !testenv.HasSrc() {
87                 t.Skip("no source code available")
88         }
89
90         if testing.Short() && testenv.Builder() == "" {
91                 t.Skip("skipping in -short mode")
92         }
93         dt := maxTime
94         nimports, _ := walkDir(t, "", time.Now().Add(dt)) // installed packages
95         t.Logf("tested %d imports", nimports)
96 }
97
98 var importedObjectTests = []struct {
99         name string
100         want string
101 }{
102         {"flag.Bool", "func Bool(name string, value bool, usage string) *bool"},
103         {"io.Reader", "type Reader interface{Read(p []byte) (n int, err error)}"},
104         {"io.ReadWriter", "type ReadWriter interface{Reader; Writer}"}, // go/types.gcCompatibilityMode is off => interface not flattened
105         {"math.Pi", "const Pi untyped float"},
106         {"math.Sin", "func Sin(x float64) float64"},
107         {"math/big.Int", "type Int struct{neg bool; abs nat}"},
108         {"golang.org/x/text/unicode/norm.MaxSegmentSize", "const MaxSegmentSize untyped int"},
109 }
110
111 func TestImportedTypes(t *testing.T) {
112         if !testenv.HasSrc() {
113                 t.Skip("no source code available")
114         }
115
116         for _, test := range importedObjectTests {
117                 i := strings.LastIndex(test.name, ".")
118                 if i < 0 {
119                         t.Fatal("invalid test data format")
120                 }
121                 importPath := test.name[:i]
122                 objName := test.name[i+1:]
123
124                 pkg, err := importer.ImportFrom(importPath, ".", 0)
125                 if err != nil {
126                         t.Error(err)
127                         continue
128                 }
129
130                 obj := pkg.Scope().Lookup(objName)
131                 if obj == nil {
132                         t.Errorf("%s: object not found", test.name)
133                         continue
134                 }
135
136                 got := types.ObjectString(obj, types.RelativeTo(pkg))
137                 if got != test.want {
138                         t.Errorf("%s: got %q; want %q", test.name, got, test.want)
139                 }
140
141                 if named, _ := obj.Type().(*types.Named); named != nil {
142                         verifyInterfaceMethodRecvs(t, named, 0)
143                 }
144         }
145 }
146
147 // verifyInterfaceMethodRecvs verifies that method receiver types
148 // are named if the methods belong to a named interface type.
149 func verifyInterfaceMethodRecvs(t *testing.T, named *types.Named, level int) {
150         // avoid endless recursion in case of an embedding bug that lead to a cycle
151         if level > 10 {
152                 t.Errorf("%s: embeds itself", named)
153                 return
154         }
155
156         iface, _ := named.Underlying().(*types.Interface)
157         if iface == nil {
158                 return // not an interface
159         }
160
161         // check explicitly declared methods
162         for i := 0; i < iface.NumExplicitMethods(); i++ {
163                 m := iface.ExplicitMethod(i)
164                 recv := m.Type().(*types.Signature).Recv()
165                 if recv == nil {
166                         t.Errorf("%s: missing receiver type", m)
167                         continue
168                 }
169                 if recv.Type() != named {
170                         t.Errorf("%s: got recv type %s; want %s", m, recv.Type(), named)
171                 }
172         }
173
174         // check embedded interfaces (they are named, too)
175         for i := 0; i < iface.NumEmbeddeds(); i++ {
176                 // embedding of interfaces cannot have cycles; recursion will terminate
177                 verifyInterfaceMethodRecvs(t, iface.Embedded(i), level+1)
178         }
179 }
180
181 func TestReimport(t *testing.T) {
182         if !testenv.HasSrc() {
183                 t.Skip("no source code available")
184         }
185
186         // Reimporting a partially imported (incomplete) package is not supported (see issue #19337).
187         // Make sure we recognize the situation and report an error.
188
189         mathPkg := types.NewPackage("math", "math") // incomplete package
190         importer := New(&build.Default, token.NewFileSet(), map[string]*types.Package{mathPkg.Path(): mathPkg})
191         _, err := importer.ImportFrom("math", ".", 0)
192         if err == nil || !strings.HasPrefix(err.Error(), "reimport") {
193                 t.Errorf("got %v; want reimport error", err)
194         }
195 }
196
197 func TestIssue20855(t *testing.T) {
198         if !testenv.HasSrc() {
199                 t.Skip("no source code available")
200         }
201
202         pkg, err := importer.ImportFrom("go/internal/srcimporter/testdata/issue20855", ".", 0)
203         if err == nil || !strings.Contains(err.Error(), "missing function body") {
204                 t.Fatalf("got unexpected or no error: %v", err)
205         }
206         if pkg == nil {
207                 t.Error("got no package despite no hard errors")
208         }
209 }
210
211 func testImportPath(t *testing.T, pkgPath string) {
212         if !testenv.HasSrc() {
213                 t.Skip("no source code available")
214         }
215
216         pkgName := path.Base(pkgPath)
217
218         pkg, err := importer.Import(pkgPath)
219         if err != nil {
220                 t.Fatal(err)
221         }
222
223         if pkg.Name() != pkgName {
224                 t.Errorf("got %q; want %q", pkg.Name(), pkgName)
225         }
226
227         if pkg.Path() != pkgPath {
228                 t.Errorf("got %q; want %q", pkg.Path(), pkgPath)
229         }
230 }
231
232 // TestIssue23092 tests relative imports.
233 func TestIssue23092(t *testing.T) {
234         testImportPath(t, "./testdata/issue23092")
235 }
236
237 // TestIssue24392 tests imports against a path containing 'testdata'.
238 func TestIssue24392(t *testing.T) {
239         testImportPath(t, "go/internal/srcimporter/testdata/issue24392")
240 }
241
242 func TestCgo(t *testing.T) {
243         testenv.MustHaveGoBuild(t)
244         testenv.MustHaveCGO(t)
245
246         buildCtx := build.Default
247         buildCtx.Dir = filepath.Join(testenv.GOROOT(t), "misc")
248         importer := New(&buildCtx, token.NewFileSet(), make(map[string]*types.Package))
249         _, err := importer.ImportFrom("./cgo/test", buildCtx.Dir, 0)
250         if err != nil {
251                 t.Fatalf("Import failed: %v", err)
252         }
253 }