golang.org/x/tools@v0.21.0/go/loader/stdlib_test.go (about)

     1  // Copyright 2013 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 loader_test
     6  
     7  // This file enumerates all packages beneath $GOROOT, loads them, plus
     8  // their external tests if any, runs the type checker on them, and
     9  // prints some summary information.
    10  
    11  import (
    12  	"bytes"
    13  	"fmt"
    14  	"go/ast"
    15  	"go/build"
    16  	"go/token"
    17  	"go/types"
    18  	"os"
    19  	"path/filepath"
    20  	"runtime"
    21  	"strings"
    22  	"testing"
    23  	"time"
    24  
    25  	"golang.org/x/tools/go/buildutil"
    26  	"golang.org/x/tools/go/loader"
    27  	"golang.org/x/tools/internal/testenv"
    28  )
    29  
    30  func TestStdlib(t *testing.T) {
    31  	if runtime.GOOS == "android" {
    32  		t.Skipf("incomplete std lib on %s", runtime.GOOS)
    33  	}
    34  	if testing.Short() {
    35  		t.Skip("skipping in short mode; uses tons of memory (https://golang.org/issue/14113)")
    36  	}
    37  	testenv.NeedsTool(t, "go")
    38  
    39  	runtime.GC()
    40  	t0 := time.Now()
    41  	var memstats runtime.MemStats
    42  	runtime.ReadMemStats(&memstats)
    43  	alloc := memstats.Alloc
    44  
    45  	// Load, parse and type-check the program.
    46  	ctxt := build.Default // copy
    47  	ctxt.GOPATH = ""      // disable GOPATH
    48  	conf := loader.Config{Build: &ctxt}
    49  	for _, path := range buildutil.AllPackages(conf.Build) {
    50  		conf.ImportWithTests(path)
    51  	}
    52  
    53  	prog, err := conf.Load()
    54  	if err != nil {
    55  		t.Fatalf("Load failed: %v", err)
    56  	}
    57  
    58  	t1 := time.Now()
    59  	runtime.GC()
    60  	runtime.ReadMemStats(&memstats)
    61  
    62  	numPkgs := len(prog.AllPackages)
    63  	if want := 205; numPkgs < want {
    64  		t.Errorf("Loaded only %d packages, want at least %d", numPkgs, want)
    65  	}
    66  
    67  	// Dump package members.
    68  	if false {
    69  		for pkg := range prog.AllPackages {
    70  			fmt.Printf("Package %s:\n", pkg.Path())
    71  			scope := pkg.Scope()
    72  			qualifier := types.RelativeTo(pkg)
    73  			for _, name := range scope.Names() {
    74  				if ast.IsExported(name) {
    75  					fmt.Printf("\t%s\n", types.ObjectString(scope.Lookup(name), qualifier))
    76  				}
    77  			}
    78  			fmt.Println()
    79  		}
    80  	}
    81  
    82  	// Check that Test functions for regexp and compress/bzip2 are
    83  	// simultaneously present. The apparent cycle formed when augmenting
    84  	// these packages by their tests (together with io/ioutil's test, which is now
    85  	// an xtest) was the original motivation or reporting golang.org/issue/7114.
    86  	//
    87  	// compress/bzip2.TestBitReader in bzip2_test.go    imports io/ioutil
    88  	// io/ioutil.TestTempFile       in tempfile_test.go imports regexp (no longer exists)
    89  	// regexp.TestRE2Search         in exec_test.go     imports compress/bzip2
    90  	for _, test := range []struct{ pkg, fn string }{
    91  		{"regexp", "TestRE2Search"},
    92  		{"compress/bzip2", "TestBitReader"},
    93  	} {
    94  		info := prog.Imported[test.pkg]
    95  		if info == nil {
    96  			t.Errorf("failed to load package %q", test.pkg)
    97  			continue
    98  		}
    99  		obj, _ := info.Pkg.Scope().Lookup(test.fn).(*types.Func)
   100  		if obj == nil {
   101  			t.Errorf("package %q has no func %q", test.pkg, test.fn)
   102  			continue
   103  		}
   104  	}
   105  
   106  	// Dump some statistics.
   107  
   108  	// determine line count
   109  	var lineCount int
   110  	prog.Fset.Iterate(func(f *token.File) bool {
   111  		lineCount += f.LineCount()
   112  		return true
   113  	})
   114  
   115  	t.Log("GOMAXPROCS:           ", runtime.GOMAXPROCS(0))
   116  	t.Log("#Source lines:        ", lineCount)
   117  	t.Log("Load/parse/typecheck: ", t1.Sub(t0))
   118  	t.Log("#MB:                  ", int64(memstats.Alloc-alloc)/1000000)
   119  }
   120  
   121  func TestCgoOption(t *testing.T) {
   122  	if testing.Short() {
   123  		t.Skip("skipping in short mode; uses tons of memory (https://golang.org/issue/14113)")
   124  	}
   125  	switch runtime.GOOS {
   126  	// On these systems, the net and os/user packages don't use cgo
   127  	// or the std library is incomplete (Android).
   128  	case "android", "plan9", "solaris", "windows":
   129  		t.Skipf("no cgo or incomplete std lib on %s", runtime.GOOS)
   130  	case "darwin":
   131  		t.Skipf("golang/go#58493: file locations in this test are stale on darwin")
   132  	}
   133  	testenv.NeedsTool(t, "go")
   134  	// In nocgo builds (e.g. linux-amd64-nocgo),
   135  	// there is no "runtime/cgo" package,
   136  	// so cgo-generated Go files will have a failing import.
   137  	testenv.NeedsTool(t, "cgo")
   138  
   139  	// Test that we can load cgo-using packages with
   140  	// CGO_ENABLED=[01], which causes go/build to select pure
   141  	// Go/native implementations, respectively, based on build
   142  	// tags.
   143  	//
   144  	// Each entry specifies a package-level object and the generic
   145  	// file expected to define it when cgo is disabled.
   146  	// When cgo is enabled, the exact file is not specified (since
   147  	// it varies by platform), but must differ from the generic one.
   148  	//
   149  	// The test also loads the actual file to verify that the
   150  	// object is indeed defined at that location.
   151  	for _, test := range []struct {
   152  		pkg, name, genericFile string
   153  	}{
   154  		{"net", "cgoLookupHost", "cgo_stub.go"},
   155  		{"os/user", "current", "lookup_stubs.go"},
   156  	} {
   157  		ctxt := build.Default
   158  		for _, ctxt.CgoEnabled = range []bool{false, true} {
   159  			conf := loader.Config{Build: &ctxt}
   160  			conf.Import(test.pkg)
   161  			prog, err := conf.Load()
   162  			if err != nil {
   163  				t.Errorf("Load failed: %v", err)
   164  				continue
   165  			}
   166  			info := prog.Imported[test.pkg]
   167  			if info == nil {
   168  				t.Errorf("package %s not found", test.pkg)
   169  				continue
   170  			}
   171  			obj := info.Pkg.Scope().Lookup(test.name)
   172  			if obj == nil {
   173  				t.Errorf("no object %s.%s", test.pkg, test.name)
   174  				continue
   175  			}
   176  			posn := prog.Fset.Position(obj.Pos())
   177  			t.Logf("%s: %s (CgoEnabled=%t)", posn, obj, ctxt.CgoEnabled)
   178  
   179  			gotFile := filepath.Base(posn.Filename)
   180  			filesMatch := gotFile == test.genericFile
   181  
   182  			if ctxt.CgoEnabled && filesMatch {
   183  				t.Errorf("CGO_ENABLED=1: %s found in %s, want native file",
   184  					obj, gotFile)
   185  			} else if !ctxt.CgoEnabled && !filesMatch {
   186  				t.Errorf("CGO_ENABLED=0: %s found in %s, want %s",
   187  					obj, gotFile, test.genericFile)
   188  			}
   189  
   190  			// Load the file and check the object is declared at the right place.
   191  			b, err := os.ReadFile(posn.Filename)
   192  			if err != nil {
   193  				t.Errorf("can't read %s: %s", posn.Filename, err)
   194  				continue
   195  			}
   196  			line := string(bytes.Split(b, []byte("\n"))[posn.Line-1])
   197  			ident := line[posn.Column-1:]
   198  			if !strings.HasPrefix(ident, test.name) {
   199  				t.Errorf("%s: %s not declared here (looking at %q)", posn, obj, ident)
   200  			}
   201  		}
   202  	}
   203  }