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

View as plain text