...
Run Format

Source file src/go/internal/srcimporter/srcimporter_test.go

Documentation: go/internal/srcimporter

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

View as plain text