github.com/AndrienkoAleksandr/go@v0.0.19/src/go/internal/srcimporter/srcimporter_test.go (about) 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 importer := New(&buildCtx, token.NewFileSet(), make(map[string]*types.Package)) 248 _, err := importer.ImportFrom("cmd/cgo/internal/test", buildCtx.Dir, 0) 249 if err != nil { 250 t.Fatalf("Import failed: %v", err) 251 } 252 }